From: Tom Pantelis Date: Wed, 13 Aug 2014 12:12:10 +0000 (+0000) Subject: Merge "Add exists method on DOMStoreReadTransaction and DOMDataReadTransaction" X-Git-Tag: release/helium~299 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=a3ff4b68093e6d675a92159e0efa2525af32d644;hp=a1ea6554ff025f333b171637b37a9ad87c7846ea Merge "Add exists method on DOMStoreReadTransaction and DOMDataReadTransaction" --- diff --git a/features/config-netty/pom.xml b/features/config-netty/pom.xml index 16fd975130..2f4b4b1e21 100644 --- a/features/config-netty/pom.xml +++ b/features/config-netty/pom.xml @@ -7,7 +7,7 @@ 0.2.5-SNAPSHOT ../../opendaylight/config/ - config-netty-features + features-config-netty pom @@ -18,11 +18,35 @@ org.opendaylight.controller - config-persister-features + features-config-persister features xml runtime + + org.opendaylight.controller + netty-event-executor-config + + + org.opendaylight.controller + netty-threadgroup-config + + + org.opendaylight.controller + netty-timer-config + + + org.opendaylight.controller + threadpool-config-api + + + org.opendaylight.controller + threadpool-config-impl + + + org.opendaylight.controller + config-netty-config + diff --git a/features/config-netty/src/main/resources/features.xml b/features/config-netty/src/main/resources/features.xml index f1b2d1f753..7f57d8cb84 100644 --- a/features/config-netty/src/main/resources/features.xml +++ b/features/config-netty/src/main/resources/features.xml @@ -3,7 +3,7 @@ - mvn:org.opendaylight.controller/config-persister-features/${config.version}/xml/features + mvn:org.opendaylight.controller/features-config-persister/${config.version}/xml/features odl-config-netty-config-api mvn:org.opendaylight.controller/netty-event-executor-config/${project.version} @@ -12,6 +12,6 @@ mvn:org.opendaylight.controller/threadpool-config-api/${project.version} mvn:org.opendaylight.controller/threadpool-config-impl/${project.version} odl-config-startup - mvn:org.opendaylight.controller/config-netty-config/${config.version}/xml/config + mvn:org.opendaylight.controller/config-netty-config/${config.version}/xml/config \ No newline at end of file diff --git a/features/config-persister/pom.xml b/features/config-persister/pom.xml index ec1520ed98..6dc8941345 100644 --- a/features/config-persister/pom.xml +++ b/features/config-persister/pom.xml @@ -7,7 +7,7 @@ 0.2.5-SNAPSHOT ../../opendaylight/config/ - config-persister-features + features-config-persister pom @@ -26,18 +26,62 @@ org.opendaylight.controller - netconf-features + features-netconf features xml runtime org.opendaylight.controller - config-features + features-config features xml runtime + + org.opendaylight.controller + config-persister-api + + + org.opendaylight.controller + config-persister-file-xml-adapter + + + org.opendaylight.controller + config-persister-impl + + + org.opendaylight.controller + config-persister-feature-adapter + + + org.opendaylight.controller + netconf-util + + + org.opendaylight.controller + netconf-mapping-api + + + com.google.guava + guava + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + org.eclipse.persistence + org.eclipse.persistence.core + + + org.eclipse.persistence + org.eclipse.persistence.moxy + diff --git a/features/config-persister/src/main/resources/features.xml b/features/config-persister/src/main/resources/features.xml index 2273a4a309..a3c005b3bd 100644 --- a/features/config-persister/src/main/resources/features.xml +++ b/features/config-persister/src/main/resources/features.xml @@ -4,22 +4,20 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0"> mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features - mvn:org.opendaylight.controller/netconf-features/${netconf.version}/xml/features - mvn:org.opendaylight.controller/config-features/${config.version}/xml/features - - odl-config-netconf-connector + mvn:org.opendaylight.controller/features-netconf/${netconf.version}/xml/features + mvn:org.opendaylight.controller/features-config/${config.version}/xml/features + odl-config-persister - odl-netconf-impl + odl-config-startup odl-netconf-api odl-config-api - yangtools-binding-generator + odl-yangtools-binding-generator mvn:org.opendaylight.controller/config-persister-api/${project.version} mvn:org.opendaylight.controller/config-persister-file-xml-adapter/${project.version} - mvn:org.opendaylight.controller/config-persister-directory-xml-adapter/${project.version} mvn:org.opendaylight.controller/config-persister-impl/${project.version} - + mvn:org.opendaylight.controller/config-persister-feature-adapter/${project.version} mvn:org.opendaylight.controller/netconf-util/${netconf.version} mvn:org.opendaylight.controller/netconf-mapping-api/${netconf.version} @@ -29,4 +27,9 @@ mvn:org.eclipse.persistence/org.eclipse.persistence.core/${eclipse.persistence.version} mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/${eclipse.persistence.version} - \ No newline at end of file + + odl-config-netconf-connector + odl-config-persister + odl-netconf-impl + + diff --git a/features/config/pom.xml b/features/config/pom.xml index 7e5dd6472b..c69e11bed2 100644 --- a/features/config/pom.xml +++ b/features/config/pom.xml @@ -7,7 +7,7 @@ 0.2.5-SNAPSHOT ../../opendaylight/config/ - config-features + features-config pom @@ -24,6 +24,74 @@ xml runtime + + org.opendaylight.controller + sal-common + + + org.opendaylight.controller + sal-common-api + + + org.opendaylight.controller + sal-common-impl + + + org.opendaylight.controller + sal-common-util + + + org.opendaylight.controller + config-api + + + org.opendaylight.controller + netty-config-api + + + io.netty + netty-transport + + + io.netty + netty-common + + + io.netty + netty-buffer + + + org.opendaylight.controller + config-util + + + org.opendaylight.controller + yang-jmx-generator + + + org.opendaylight.controller + shutdown-api + + + org.opendaylight.controller + shutdown-impl + + + org.osgi + org.osgi.core + + + com.google.guava + guava + + + org.javassist + javassist + + + org.opendaylight.controller + config-manager + diff --git a/features/config/src/main/resources/features.xml b/features/config/src/main/resources/features.xml index de5b198173..6c0d32427d 100644 --- a/features/config/src/main/resources/features.xml +++ b/features/config/src/main/resources/features.xml @@ -5,45 +5,52 @@ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0"> mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features - - yangtools-concepts - yangtools-binding - yangtools-binding-generator - odl-mdsal-commons - odl-config-api - mvn:org.opendaylight.controller/config-util/${project.version} - mvn:org.opendaylight.controller/yang-jmx-generator/${project.version} - mvn:org.opendaylight.controller/shutdown-api/${project.version} - mvn:org.opendaylight.controller/shutdown-impl/${project.version} - mvn:org.osgi/org.osgi.core/${osgi.core.version} - mvn:com.google.guava/guava/${guava.version} - mvn:org.javassist/javassist/${javassist.version} + + odl-mdsal-common + odl-config-api + odl-config-netty-config-api + odl-config-core + odl-config-manager - - odl-config-core - mvn:org.opendaylight.controller/config-manager/${project.version} + + + odl-yangtools-data-binding + mvn:org.opendaylight.controller/sal-common/${mdsal.version} + mvn:org.opendaylight.controller/sal-common-api/${mdsal.version} + mvn:org.opendaylight.controller/sal-common-impl/${mdsal.version} + mvn:org.opendaylight.controller/sal-common-util/${mdsal.version} mvn:org.opendaylight.controller/config-api/${project.version} - - - yangtools-concepts - yangtools-binding + odl-yangtools-common + odl-yangtools-binding + odl-config-api mvn:org.opendaylight.controller/netty-config-api/${project.version} - - mvn:io.netty/netty-transport/${netty.version} mvn:io.netty/netty-common/${netty.version} mvn:io.netty/netty-buffer/${netty.version} + + + odl-yangtools-common + odl-yangtools-binding + odl-yangtools-binding-generator + odl-mdsal-common odl-config-api + mvn:org.opendaylight.controller/config-util/${project.version} + mvn:org.opendaylight.controller/yang-jmx-generator/${project.version} + mvn:org.opendaylight.controller/shutdown-api/${project.version} + mvn:org.opendaylight.controller/shutdown-impl/${project.version} + mvn:org.osgi/org.osgi.core/${osgi.core.version} + mvn:com.google.guava/guava/${guava.version} + mvn:org.javassist/javassist/${javassist.version} - - mvn:org.opendaylight.controller/netconf-config-dispatcher/${project.version} + + odl-config-core + mvn:org.opendaylight.controller/config-manager/${project.version} - \ No newline at end of file diff --git a/features/flow/pom.xml b/features/flow/pom.xml new file mode 100644 index 0000000000..09bb6c91e6 --- /dev/null +++ b/features/flow/pom.xml @@ -0,0 +1,120 @@ + + + 4.0.0 + + org.opendaylight.controller + sal-parent + 1.1-SNAPSHOT + ../../opendaylight/md-sal + + features-flow + + pom + + + features.xml + + + + + org.opendaylight.controller + features-mdsal + ${mdsal.version} + features + xml + runtime + + + org.opendaylight.controller.model + model-flow-base + + + org.opendaylight.controller.model + model-flow-service + + + org.opendaylight.controller.model + model-flow-statistics + + + org.opendaylight.controller.model + model-inventory + + + org.opendaylight.controller.model + model-topology + + + org.opendaylight.controller.md + topology-manager + + + org.opendaylight.controller.md + topology-lldp-discovery + + + org.opendaylight.controller.md + statistics-manager + + + org.opendaylight.controller.md + inventory-manager + + + org.opendaylight.controller.md + forwardingrules-manager + + + + + + + true + src/main/resources + + + + + org.apache.maven.plugins + maven-resources-plugin + + + filter + + resources + + generate-resources + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/${features.file} + xml + features + + + + + + + + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + HEAD + https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL + + diff --git a/features/flow/src/main/resources/features.xml b/features/flow/src/main/resources/features.xml new file mode 100644 index 0000000000..3f914be4ae --- /dev/null +++ b/features/flow/src/main/resources/features.xml @@ -0,0 +1,26 @@ + + + + mvn:org.opendaylight.controller/features-mdsal/${mdsal.version}/xml/features + + odl-yangtools-models + mvn:org.opendaylight.controller.model/model-flow-base/${project.version} + mvn:org.opendaylight.controller.model/model-flow-service/${project.version} + mvn:org.opendaylight.controller.model/model-flow-statistics/${project.version} + mvn:org.opendaylight.controller.model/model-inventory/${project.version} + mvn:org.opendaylight.controller.model/model-topology/${project.version} + + + odl-mdsal-broker + odl-flow-model + mvn:org.opendaylight.controller.md/topology-manager/${project.version} + mvn:org.opendaylight.controller.md/topology-lldp-discovery/${project.version} + mvn:org.opendaylight.controller.md/statistics-manager/${project.version} + mvn:org.opendaylight.controller.md/inventory-manager/${project.version} + mvn:org.opendaylight.controller.md/forwardingrules-manager/${project.version} + mvn:org.opendaylight.controller/liblldp/${sal.version} + + + diff --git a/features/mdsal/pom.xml b/features/mdsal/pom.xml index 2983c5efab..4f1ba98e5c 100644 --- a/features/mdsal/pom.xml +++ b/features/mdsal/pom.xml @@ -7,7 +7,7 @@ 1.1-SNAPSHOT ../../opendaylight/md-sal - mdsal-features + features-mdsal pom @@ -15,7 +15,164 @@ features.xml - + + + org.opendaylight.yangtools + features-yangtools + features + xml + runtime + + + org.opendaylight.controller + features-config + features + xml + runtime + + + org.opendaylight.controller + features-config-persister + features + xml + runtime + + + org.opendaylight.controller + features-config-netty + features + xml + runtime + + + org.opendaylight.controller + sal-core-api + + + org.opendaylight.controller + sal-core-api + + + org.opendaylight.controller + sal-core-spi + + + org.opendaylight.controller + sal-broker-impl + + + org.opendaylight.controller + sal-binding-api + + + org.opendaylight.controller + sal-binding-config + + + org.opendaylight.controller + sal-binding-broker-impl + + + org.opendaylight.controller + sal-binding-util + + + org.opendaylight.controller + sal-connector-api + + + org.opendaylight.controller + sal-inmemory-datastore + + + org.opendaylight.controller + md-sal-config + + + org.opendaylight.controller + sal-netconf-connector + + + org.opendaylight.controller.model + model-inventory + + + org.opendaylight.controller + netconf-config-dispatcher + + + org.opendaylight.controller + netconf-connector-config + + + org.opendaylight.controller + sal-rest-connector + + + com.google.code.gson + gson + + + com.sun.jersey + jersey-core + + + com.sun.jersey + jersey-server + + + org.opendaylight.controller.thirdparty + com.sun.jersey.jersey-servlet + + + io.netty + netty-buffer + + + io.netty + netty-codec + + + io.netty + netty-codec-http + + + io.netty + netty-common + + + io.netty + netty-handler + + + io.netty + netty-transport + + + org.opendaylight.controller + sal-remote + + + org.opendaylight.controller + sal-rest-connector-config + + + org.opendaylight.controller.samples + sample-toaster + + + org.opendaylight.controller.samples + sample-toaster-provider + + + org.opendaylight.controller.samples + sample-toaster-consumer + + + org.opendaylight.controller.samples + toaster-config + + diff --git a/features/mdsal/src/main/resources/features.xml b/features/mdsal/src/main/resources/features.xml index 7d393bc64c..a3d7ed0f83 100644 --- a/features/mdsal/src/main/resources/features.xml +++ b/features/mdsal/src/main/resources/features.xml @@ -1,28 +1,24 @@ - + mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features + mvn:org.opendaylight.controller/features-config/${config.version}/xml/features + mvn:org.opendaylight.controller/features-config-persister/${config.version}/xml/features + mvn:org.opendaylight.controller/features-config-netty/${config.version}/xml/features - odl-mdsal-commons odl-mdsal-broker - odl-mdsal-restconf - - - yangtools-data-binding - mvn:org.opendaylight.controller/sal-common/${project.version} - mvn:org.opendaylight.controller/sal-common-api/${project.version} - mvn:org.opendaylight.controller/sal-common-impl/${project.version} - mvn:org.opendaylight.controller/sal-common-util/${project.version} + odl-mdsal-netconf-connector + odl-restconf + odl-toaster - yangtools-concepts - yangtools-binding - odl-mdsal-commons - odl-config-core - odl-config-manager - odl-config-api - odl-config-persister + odl-yangtools-common + odl-yangtools-binding + odl-mdsal-common + odl-config-startup + odl-config-netty mvn:org.opendaylight.controller/sal-core-api/${project.version} mvn:org.opendaylight.controller/sal-core-spi/${project.version} mvn:org.opendaylight.controller/sal-broker-impl/${project.version} @@ -32,47 +28,41 @@ mvn:org.opendaylight.controller/sal-binding-util/${project.version} mvn:org.opendaylight.controller/sal-connector-api/${project.version} mvn:org.opendaylight.controller/sal-inmemory-datastore/${project.version} + mvn:org.opendaylight.controller/md-sal-config/${mdsal.version}/xml/config + + + odl-mdsal-broker + odl-netconf-client + odl-yangtools-models + mvn:org.opendaylight.controller/sal-netconf-connector/${project.version} + mvn:org.opendaylight.controller.model/model-inventory/${project.version} + mvn:org.opendaylight.controller/netconf-config-dispatcher/${config.version} + mvn:org.opendaylight.controller/netconf-connector-config/${netconf.version}/xml/config - + odl-mdsal-broker + war mvn:org.opendaylight.controller/sal-rest-connector/${project.version} - wrap:mvn:com.google.code.gson/gson/${gson.version} - wrap:mvn:com.sun.jersey/jersey-core/${jersey.version} - wrap:mvn:com.sun.jersey/jersey-server/${jersey.version} + mvn:com.google.code.gson/gson/${gson.version} + mvn:com.sun.jersey/jersey-core/${jersey.version} + mvn:com.sun.jersey/jersey-server/${jersey.version} mvn:org.opendaylight.controller.thirdparty/com.sun.jersey.jersey-servlet/${jersey.version} - wrap:mvn:io.netty/netty-buffer/${netty.version} - wrap:mvn:io.netty/netty-codec/${netty.version} - wrap:mvn:io.netty/netty-codec-http/${netty.version} - wrap:mvn:io.netty/netty-common/${netty.version} - wrap:mvn:io.netty/netty-handler/${netty.version} - wrap:mvn:io.netty/netty-transport/${netty.version} - - - mvn:org.opendaylight.controller.model/model-flow-base/${project.version} - mvn:org.opendaylight.controller.model/model-flow-management/${project.version} - mvn:org.opendaylight.controller.model/model-flow-service/${project.version} - mvn:org.opendaylight.controller.model/model-flow-statistics/${project.version} - mvn:org.opendaylight.controller.model/model-inventory/${project.version} - mvn:org.opendaylight.controller.model/model-topology/${project.version} + mvn:io.netty/netty-buffer/${netty.version} + mvn:io.netty/netty-codec/${netty.version} + mvn:io.netty/netty-codec-http/${netty.version} + mvn:io.netty/netty-common/${netty.version} + mvn:io.netty/netty-handler/${netty.version} + mvn:io.netty/netty-transport/${netty.version} + mvn:org.opendaylight.controller/sal-remote/${project.version} + mvn:org.opendaylight.controller/sal-rest-connector-config/${mdsal.version}/xml/config - - yangtools-concepts - yangtools-binding + + odl-yangtools-common + odl-yangtools-binding odl-mdsal-broker - odl-mdsal-all mvn:org.opendaylight.controller.samples/sample-toaster/${project.version} mvn:org.opendaylight.controller.samples/sample-toaster-consumer/${project.version} mvn:org.opendaylight.controller.samples/sample-toaster-provider/${project.version} + mvn:org.opendaylight.controller.samples/toaster-config/${project.version}/xml/config - - mvn:org.opendaylight.controller/sal-netconf-connector/${project.version} - mvn:org.opendaylight.controller/sal-restconf-broker/${project.version} - mvn:org.opendaylight.controller/sal-remote/${project.version} - mvn:org.opendaylight.controller.md/topology-manager/${project.version} - mvn:org.opendaylight.controller.md/topology-lldp-discovery/${project.version} - mvn:org.opendaylight.controller.md/statistics-manager/${project.version} - mvn:org.opendaylight.controller.md/inventory-manager/${project.version} - mvn:org.opendaylight.controller.md/forwardingrules-manager/${project.version} - - diff --git a/features/netconf/pom.xml b/features/netconf/pom.xml index 856557c1e8..956a67e28b 100644 --- a/features/netconf/pom.xml +++ b/features/netconf/pom.xml @@ -7,7 +7,7 @@ 0.2.5-SNAPSHOT ../../opendaylight/netconf - netconf-features + features-netconf pom @@ -18,18 +18,98 @@ org.opendaylight.controller - config-features + features-config features xml runtime org.opendaylight.controller - features-odl-protocol-framework + features-protocol-framework features xml runtime + + org.opendaylight.controller + netconf-api + + + org.opendaylight.controller + ietf-netconf-monitoring + + + org.opendaylight.controller + ietf-netconf-monitoring-extension + + + org.opendaylight.yangtools.model + ietf-inet-types + + + org.opendaylight.yangtools.model + ietf-yang-types + + + org.opendaylight.controller + netconf-mapping-api + + + org.opendaylight.controller + netconf-util + + + org.opendaylight.controller + netconf-impl + + + org.opendaylight.controller + config-netconf-connector + + + org.opendaylight.controller + netconf-netty-util + + + org.opendaylight.controller.thirdparty + ganymed + + + org.openexi + nagasena + + + io.netty + netty-codec + + + io.netty + netty-handler + + + io.netty + netty-common + + + io.netty + netty-buffer + + + io.netty + netty-transport + + + org.opendaylight.controller + netconf-client + + + org.opendaylight.controller + netconf-config + + + org.opendaylight.controller + netconf-monitoring + diff --git a/features/netconf/src/main/resources/features.xml b/features/netconf/src/main/resources/features.xml index 50a537b50a..0033b0d83c 100644 --- a/features/netconf/src/main/resources/features.xml +++ b/features/netconf/src/main/resources/features.xml @@ -3,14 +3,24 @@ - mvn:org.opendaylight.controller/features-odl-protocol-framework/${protocol-framework.version}/xml/features - mvn:org.opendaylight.controller/config-features/${config.version}/xml/features + mvn:org.opendaylight.controller/features-protocol-framework/${protocol-framework.version}/xml/features + mvn:org.opendaylight.controller/features-config/${config.version}/xml/features + + odl-netconf-api + odl-netconf-mapping-api + odl-netconf-util + odl-netconf-impl + odl-config-netconf-connector + odl-netconf-netty-util + odl-netconf-client + odl-netconf-monitoring + + odl-protocol-framework mvn:org.opendaylight.controller/netconf-api/${project.version} mvn:org.opendaylight.controller/ietf-netconf-monitoring/${project.version} mvn:org.opendaylight.controller/ietf-netconf-monitoring-extension/${project.version} - odl-protocol-framework mvn:org.opendaylight.yangtools.model/ietf-inet-types/${ietf-inet-types.version} mvn:org.opendaylight.yangtools.model/ietf-yang-types/${ietf-yang-types.version} @@ -22,26 +32,25 @@ odl-netconf-mapping-api mvn:org.opendaylight.controller/netconf-util/${project.version} - - odl-config-manager - mvn:org.opendaylight.controller/config-netconf-connector/${project.version} + odl-netconf-api odl-netconf-mapping-api odl-netconf-util - - - + odl-netconf-netty-util mvn:org.opendaylight.controller/netconf-impl/${project.version} + + + odl-config-manager odl-netconf-api odl-netconf-mapping-api odl-netconf-util - odl-netconf-netty-util + mvn:org.opendaylight.controller/config-netconf-connector/${project.version} - mvn:org.opendaylight.controller/netconf-netty-util/${project.version} odl-netconf-api odl-netconf-mapping-api odl-netconf-util + mvn:org.opendaylight.controller/netconf-netty-util/${project.version} mvn:org.opendaylight.controller.thirdparty/ganymed/${ganymed.version} mvn:org.openexi/nagasena/${exi.nagasena.version} mvn:io.netty/netty-codec/${netty.version} @@ -50,10 +59,14 @@ mvn:io.netty/netty-buffer/${netty.version} mvn:io.netty/netty-transport/${netty.version} - + + odl-netconf-netty-util mvn:org.opendaylight.controller/netconf-client/${project.version} + mvn:org.opendaylight.controller/netconf-config/${netconf.version}/xml/config + + + odl-netconf-util mvn:org.opendaylight.controller/netconf-monitoring/${project.version} - mvn:org.opendaylight.controller/netconf-tcp/${project.version} \ No newline at end of file diff --git a/features/pom.xml b/features/pom.xml index f69190cebd..88ed7491a7 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -22,6 +22,7 @@ config-persister config-netty mdsal + flow netconf protocol-framework diff --git a/features/protocol-framework/pom.xml b/features/protocol-framework/pom.xml index ba5dd18fc2..97836be455 100644 --- a/features/protocol-framework/pom.xml +++ b/features/protocol-framework/pom.xml @@ -7,7 +7,7 @@ 1.4.2-SNAPSHOT ../../opendaylight/commons/opendaylight - features-odl-protocol-framework + features-protocol-framework ${protocol-framework.version} pom @@ -18,11 +18,15 @@ org.opendaylight.controller - config-features + features-config features xml runtime + + org.opendaylight.controller + protocol-framework + diff --git a/features/protocol-framework/src/main/resources/features.xml b/features/protocol-framework/src/main/resources/features.xml index d2560f5cb0..6daa3432c1 100644 --- a/features/protocol-framework/src/main/resources/features.xml +++ b/features/protocol-framework/src/main/resources/features.xml @@ -3,10 +3,10 @@ - mvn:org.opendaylight.controller/config-features/${config.version}/xml/features + mvn:org.opendaylight.controller/features-config/${config.version}/xml/features + odl-config-api + odl-config-netty-config-api mvn:org.opendaylight.controller/protocol-framework/${protocol-framework.version} - odl-config-api - odl-config-netty-config-api \ No newline at end of file diff --git a/opendaylight/commons/liblldp/pom.xml b/opendaylight/commons/liblldp/pom.xml new file mode 100644 index 0000000000..1551041edb --- /dev/null +++ b/opendaylight/commons/liblldp/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.2-SNAPSHOT + ../opendaylight + + + liblldp + 0.8.1-SNAPSHOT + bundle + + + junit + junit + + + org.apache.commons + commons-lang3 + + + org.slf4j + slf4j-api + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + org.slf4j, + org.apache.commons.lang3.builder, + org.apache.commons.lang3.tuple + + + org.opendaylight.controller.liblldp + + ${project.basedir}/META-INF + + + + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + HEAD + https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main + + diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/BitBufferHelper.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/BitBufferHelper.java new file mode 100644 index 0000000000..3eae43212f --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/BitBufferHelper.java @@ -0,0 +1,718 @@ +/* + * 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.liblldp; + +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * BitBufferHelper class that provides utility methods to + * - fetch specific bits from a serialized stream of bits + * - convert bits to primitive data type - like short, int, long + * - store bits in specified location in stream of bits + * - convert primitive data types to stream of bits + */ +public abstract class BitBufferHelper { + protected static final Logger logger = LoggerFactory + .getLogger(BitBufferHelper.class); + + public static final long ByteMask = 0xFF; + + // Getters + // data: array where data are stored + // startOffset: bit from where to start reading + // numBits: number of bits to read + // All this function return an exception if overflow or underflow + + /** + * Returns the first byte from the byte array + * @param byte[] data + * @return byte value + */ + public static byte getByte(byte[] data) { + if ((data.length * NetUtils.NumBitsInAByte) > Byte.SIZE) { + try { + throw new BufferException( + "Container is too small for the number of requested bits"); + } catch (BufferException e) { + logger.error("", e); + } + } + return (data[0]); + } + + /** + * Returns the short value for the byte array passed. + * Size of byte array is restricted to Short.SIZE + * @param byte[] data + * @return short value + */ + public static short getShort(byte[] data) { + if (data.length > Short.SIZE) { + try { + throw new BufferException( + "Container is too small for the number of requested bits"); + } catch (BufferException e) { + logger.error("", e); + } + } + return (short) toNumber(data); + } + + /** + * Returns the int value for the byte array passed. + * Size of byte array is restricted to Integer.SIZE + * @param byte[] data + * @return int - the integer value of byte array + */ + public static int getInt(byte[] data) { + if (data.length > Integer.SIZE) { + try { + throw new BufferException( + "Container is too small for the number of requested bits"); + } catch (BufferException e) { + logger.error("", e); + } + } + return (int) toNumber(data); + } + + /** + * Returns the long value for the byte array passed. + * Size of byte array is restricted to Long.SIZE + * @param byte[] data + * @return long - the integer value of byte array + */ + public static long getLong(byte[] data) { + if (data.length > Long.SIZE) { + try { + throw new BufferException( + "Container is too small for the number of requested bits"); + } catch (Exception e) { + logger.error("", e); + } + } + return (long) toNumber(data); + } + + /** + * Returns the short value for the last numBits of the byte array passed. + * Size of numBits is restricted to Short.SIZE + * @param byte[] data + * @param int - numBits + * @return short - the short value of byte array + */ + public static short getShort(byte[] data, int numBits) { + if (numBits > Short.SIZE) { + try { + throw new BufferException( + "Container is too small for the number of requested bits"); + } catch (BufferException e) { + logger.error("", e); + } + } + int startOffset = data.length * NetUtils.NumBitsInAByte - numBits; + byte[] bits = null; + try { + bits = BitBufferHelper.getBits(data, startOffset, numBits); + } catch (BufferException e) { + logger.error("", e); + } + return (short) toNumber(bits, numBits); + } + + /** + * Returns the int value for the last numBits of the byte array passed. + * Size of numBits is restricted to Integer.SIZE + * @param byte[] data + * @param int - numBits + * @return int - the integer value of byte array + */ + public static int getInt(byte[] data, int numBits) { + if (numBits > Integer.SIZE) { + try { + throw new BufferException( + "Container is too small for the number of requested bits"); + } catch (BufferException e) { + logger.error("", e); + } + } + int startOffset = data.length * NetUtils.NumBitsInAByte - numBits; + byte[] bits = null; + try { + bits = BitBufferHelper.getBits(data, startOffset, numBits); + } catch (BufferException e) { + logger.error("", e); + } + return (int) toNumber(bits, numBits); + } + + /** + * Returns the long value for the last numBits of the byte array passed. + * Size of numBits is restricted to Long.SIZE + * @param byte[] data + * @param int - numBits + * @return long - the integer value of byte array + */ + public static long getLong(byte[] data, int numBits) { + if (numBits > Long.SIZE) { + try { + throw new BufferException( + "Container is too small for the number of requested bits"); + } catch (BufferException e) { + logger.error("", e); + } + } + if (numBits > data.length * NetUtils.NumBitsInAByte) { + try { + throw new BufferException( + "Trying to read more bits than contained in the data buffer"); + } catch (BufferException e) { + logger.error("", e); + } + } + int startOffset = data.length * NetUtils.NumBitsInAByte - numBits; + byte[] bits = null; + try { + bits = BitBufferHelper.getBits(data, startOffset, numBits); + } catch (BufferException e) { + logger.error("", e); + } + return (long) toNumber(bits, numBits); + } + + /** + * Reads the specified number of bits from the passed byte array + * starting to read from the specified offset + * The bits read are stored in a byte array which size is dictated + * by the number of bits to be stored. + * The bits are stored in the byte array LSB aligned. + * + * Ex. + * Read 7 bits at offset 10 + * 0 9 10 16 17 + * 0101000010 | 0000101 | 1111001010010101011 + * will be returned as {0,0,0,0,0,1,0,1} + * + * @param byte[] data + * @param int startOffset - offset to start fetching bits from data from + * @param int numBits - number of bits to be fetched from data + * @return byte [] - LSB aligned bits + * + * @throws BufferException + * when the startOffset and numBits parameters are not congruent + * with the data buffer size + */ + public static byte[] getBits(byte[] data, int startOffset, int numBits) + throws BufferException { + + int startByteOffset = 0; + int valfromcurr, valfromnext; + int extranumBits = numBits % NetUtils.NumBitsInAByte; + int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte; + int numBytes = (numBits % NetUtils.NumBitsInAByte != 0) ? 1 + numBits + / NetUtils.NumBitsInAByte : numBits / NetUtils.NumBitsInAByte; + byte[] shiftedBytes = new byte[numBytes]; + startByteOffset = startOffset / NetUtils.NumBitsInAByte; + byte[] bytes = new byte[numBytes]; + if (numBits == 0) { + return bytes; + } + + checkExceptions(data, startOffset, numBits); + + if (extraOffsetBits == 0) { + if (extranumBits == 0) { + System.arraycopy(data, startByteOffset, bytes, 0, numBytes); + return bytes; + } else { + System.arraycopy(data, startByteOffset, bytes, 0, numBytes - 1); + bytes[numBytes - 1] = (byte) ((int) data[startByteOffset + + numBytes - 1] & getMSBMask(extranumBits)); + } + } else { + int i; + for (i = 0; i < numBits / NetUtils.NumBitsInAByte; i++) { + // Reading numBytes starting from offset + valfromcurr = (data[startByteOffset + i]) + & getLSBMask(NetUtils.NumBitsInAByte - extraOffsetBits); + valfromnext = (data[startByteOffset + i + 1]) + & getMSBMask(extraOffsetBits); + bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits))); + } + // Now adding the rest of the bits if any + if (extranumBits != 0) { + if (extranumBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) { + valfromnext = (byte) (data[startByteOffset + i] & ((getMSBMask(extranumBits)) >> extraOffsetBits)); + bytes[i] = (byte) (valfromnext << extraOffsetBits); + } else if (extranumBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) { + valfromcurr = (data[startByteOffset + i]) + & getLSBMask(NetUtils.NumBitsInAByte + - extraOffsetBits); + bytes[i] = (byte) (valfromcurr << extraOffsetBits); + } else { + valfromcurr = (data[startByteOffset + i]) + & getLSBMask(NetUtils.NumBitsInAByte + - extraOffsetBits); + valfromnext = (data[startByteOffset + i + 1]) + & (getMSBMask(extranumBits + - (NetUtils.NumBitsInAByte - extraOffsetBits))); + bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits))); + } + + } + } + // Aligns the bits to LSB + shiftedBytes = shiftBitsToLSB(bytes, numBits); + return shiftedBytes; + } + + // Setters + // data: array where data will be stored + // input: the data that need to be stored in the data array + // startOffset: bit from where to start writing + // numBits: number of bits to read + + /** + * Bits are expected to be stored in the input byte array from LSB + * @param byte[] - data to set the input byte + * @param byte - input byte to be inserted + * @param startOffset - offset of data[] to start inserting byte from + * @param numBits - number of bits of input to be inserted into data[] + * + * @throws BufferException + * when the input, startOffset and numBits are not congruent + * with the data buffer size + */ + public static void setByte(byte[] data, byte input, int startOffset, + int numBits) throws BufferException { + byte[] inputByteArray = new byte[1]; + Arrays.fill(inputByteArray, 0, 1, input); + setBytes(data, inputByteArray, startOffset, numBits); + } + + /** + * Bits are expected to be stored in the input byte array from LSB + * @param byte[] - data to set the input byte + * @param byte[] - input bytes to be inserted + * @param startOffset - offset of data[] to start inserting byte from + * @param numBits - number of bits of input to be inserted into data[] + * @return void + * @throws BufferException + * when the startOffset and numBits parameters are not congruent + * with data and input buffers' size + */ + public static void setBytes(byte[] data, byte[] input, int startOffset, + int numBits) throws BufferException { + checkExceptions(data, startOffset, numBits); + insertBits(data, input, startOffset, numBits); + } + + /** + * Returns numBits 1's in the MSB position + * + * @param numBits + * @return + */ + public static int getMSBMask(int numBits) { + int mask = 0; + for (int i = 0; i < numBits; i++) { + mask = mask | (1 << (7 - i)); + } + return mask; + } + + /** + * Returns numBits 1's in the LSB position + * + * @param numBits + * @return + */ + public static int getLSBMask(int numBits) { + int mask = 0; + for (int i = 0; i < numBits; i++) { + mask = mask | (1 << i); + } + return mask; + } + + /** + * Returns the numerical value of the byte array passed + * + * @param byte[] - array + * @return long - numerical value of byte array passed + */ + static public long toNumber(byte[] array) { + long ret = 0; + long length = array.length; + int value = 0; + for (int i = 0; i < length; i++) { + value = array[i]; + if (value < 0) + value += 256; + ret = ret + | (long) ((long) value << ((length - i - 1) * NetUtils.NumBitsInAByte)); + } + return ret; + } + + /** + * Returns the numerical value of the last numBits (LSB bits) of the byte + * array passed + * + * @param byte[] - array + * @param int - numBits + * @return long - numerical value of byte array passed + */ + static public long toNumber(byte[] array, int numBits) { + int length = numBits / NetUtils.NumBitsInAByte; + int bitsRest = numBits % NetUtils.NumBitsInAByte; + int startOffset = array.length - length; + long ret = 0; + int value = 0; + + value = array[startOffset - 1] & getLSBMask(bitsRest); + value = (array[startOffset - 1] < 0) ? (array[startOffset - 1] + 256) + : array[startOffset - 1]; + ret = ret + | (value << ((array.length - startOffset) * NetUtils.NumBitsInAByte)); + + for (int i = startOffset; i < array.length; i++) { + value = array[i]; + if (value < 0) + value += 256; + ret = ret + | (long) ((long) value << ((array.length - i - 1) * NetUtils.NumBitsInAByte)); + } + + return ret; + } + + /** + * Accepts a number as input and returns its value in byte form in LSB + * aligned form example: input = 5000 [1001110001000] bytes = 19, -120 + * [00010011] [10001000] + * + * @param Number + * @return byte[] + * + */ + + public static byte[] toByteArray(Number input) { + Class dataType = input.getClass(); + short size = 0; + long longValue = input.longValue(); + + if (dataType == Byte.class || dataType == byte.class) { + size = Byte.SIZE; + } else if (dataType == Short.class || dataType == short.class) { + size = Short.SIZE; + } else if (dataType == Integer.class || dataType == int.class) { + size = Integer.SIZE; + } else if (dataType == Long.class || dataType == long.class) { + size = Long.SIZE; + } else { + throw new IllegalArgumentException( + "Parameter must one of the following: Short/Int/Long\n"); + } + + int length = size / NetUtils.NumBitsInAByte; + byte bytes[] = new byte[length]; + + // Getting the bytes from input value + for (int i = 0; i < length; i++) { + bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length + - i - 1))) & ByteMask); + } + return bytes; + } + + /** + * Accepts a number as input and returns its value in byte form in MSB + * aligned form example: input = 5000 [1001110001000] bytes = -114, 64 + * [10011100] [01000000] + * + * @param Number + * input + * @param int numBits - the number of bits to be returned + * @return byte[] + * + */ + public static byte[] toByteArray(Number input, int numBits) { + Class dataType = input.getClass(); + short size = 0; + long longValue = input.longValue(); + + if (dataType == Short.class) { + size = Short.SIZE; + } else if (dataType == Integer.class) { + size = Integer.SIZE; + } else if (dataType == Long.class) { + size = Long.SIZE; + } else { + throw new IllegalArgumentException( + "Parameter must one of the following: Short/Int/Long\n"); + } + + int length = size / NetUtils.NumBitsInAByte; + byte bytes[] = new byte[length]; + byte[] inputbytes = new byte[length]; + byte shiftedBytes[]; + + // Getting the bytes from input value + for (int i = 0; i < length; i++) { + bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length + - i - 1))) & ByteMask); + } + + if ((bytes[0] == 0 && dataType == Long.class) + || (bytes[0] == 0 && dataType == Integer.class)) { + int index = 0; + for (index = 0; index < length; ++index) { + if (bytes[index] != 0) { + bytes[0] = bytes[index]; + break; + } + } + System.arraycopy(bytes, index, inputbytes, 0, length - index); + Arrays.fill(bytes, length - index + 1, length - 1, (byte) 0); + } else { + System.arraycopy(bytes, 0, inputbytes, 0, length); + } + + shiftedBytes = shiftBitsToMSB(inputbytes, numBits); + + return shiftedBytes; + } + + /** + * Takes an LSB aligned byte array and returned the LSB numBits in a MSB + * aligned byte array + * + * @param inputbytes + * @param numBits + * @return + */ + /** + * It aligns the last numBits bits to the head of the byte array following + * them with numBits % 8 zero bits. + * + * Example: For inputbytes = [00000111][01110001] and numBits = 12 it + * returns: shiftedBytes = [01110111][00010000] + * + * @param byte[] inputBytes + * @param int numBits - number of bits to be left aligned + * @return byte[] + */ + public static byte[] shiftBitsToMSB(byte[] inputBytes, int numBits) { + int numBitstoShiftBy = 0, leadZeroesMSB = 8, numEndRestBits = 0; + int size = inputBytes.length; + byte[] shiftedBytes = new byte[size]; + int i; + + for (i = 0; i < Byte.SIZE; i++) { + if (((byte) (inputBytes[0] & getMSBMask(i + 1))) != 0) { + leadZeroesMSB = i; + break; + } + } + + if (numBits % NetUtils.NumBitsInAByte == 0) { + numBitstoShiftBy = 0; + } else { + numBitstoShiftBy = ((NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte)) < leadZeroesMSB) ? (NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte)) + : leadZeroesMSB; + } + if (numBitstoShiftBy == 0) { + return inputBytes; + } + + if (numBits < NetUtils.NumBitsInAByte) { + // inputbytes.length = 1 OR read less than a byte + shiftedBytes[0] = (byte) ((inputBytes[0] & getLSBMask(numBits)) << numBitstoShiftBy); + } else { + // # of bits to read from last byte + numEndRestBits = NetUtils.NumBitsInAByte + - (inputBytes.length * NetUtils.NumBitsInAByte - numBits - numBitstoShiftBy); + + for (i = 0; i < (size - 1); i++) { + if ((i + 1) == (size - 1)) { + if (numEndRestBits > numBitstoShiftBy) { + shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (numEndRestBits - numBitstoShiftBy))); + shiftedBytes[i + 1] = (byte) ((inputBytes[i + 1] & getLSBMask(numEndRestBits + - numBitstoShiftBy)) << numBitstoShiftBy); + } else + shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numEndRestBits)) >> (NetUtils.NumBitsInAByte - numEndRestBits))); + } + shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (NetUtils.NumBitsInAByte - numBitstoShiftBy)); + } + + } + return shiftedBytes; + } + + /** + * It aligns the first numBits bits to the right end of the byte array + * preceding them with numBits % 8 zero bits. + * + * Example: For inputbytes = [01110111][00010000] and numBits = 12 it + * returns: shiftedBytes = [00000111][01110001] + * + * @param byte[] inputBytes + * @param int numBits - number of bits to be right aligned + * @return byte[] + */ + public static byte[] shiftBitsToLSB(byte[] inputBytes, int numBits) { + int numBytes = inputBytes.length; + int numBitstoShift = numBits % NetUtils.NumBitsInAByte; + byte[] shiftedBytes = new byte[numBytes]; + int inputLsb = 0, inputMsb = 0; + + if (numBitstoShift == 0) { + return inputBytes; + } + + for (int i = 1; i < numBytes; i++) { + inputLsb = inputBytes[i - 1] + & getLSBMask(NetUtils.NumBitsInAByte - numBitstoShift); + inputLsb = (inputLsb < 0) ? (inputLsb + 256) : inputLsb; + inputMsb = inputBytes[i] & getMSBMask(numBitstoShift); + inputMsb = (inputBytes[i] < 0) ? (inputBytes[i] + 256) + : inputBytes[i]; + shiftedBytes[i] = (byte) ((inputLsb << numBitstoShift) | (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift))); + } + inputMsb = inputBytes[0] & (getMSBMask(numBitstoShift)); + inputMsb = (inputMsb < 0) ? (inputMsb + 256) : inputMsb; + shiftedBytes[0] = (byte) (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift)); + return shiftedBytes; + } + + /** + * Insert in the data buffer at position dictated by the offset the number + * of bits specified from the input data byte array. The input byte array + * has the bits stored starting from the LSB + * + * @param byte[] data + * @param byte[] inputdata + * @param int startOffset + * @param int numBits + */ + public static void insertBits(byte[] data, byte[] inputdataLSB, + int startOffset, int numBits) { + byte[] inputdata = shiftBitsToMSB(inputdataLSB, numBits); // Align to + // MSB the + // passed byte + // array + int numBytes = numBits / NetUtils.NumBitsInAByte; + int startByteOffset = startOffset / NetUtils.NumBitsInAByte; + int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte; + int extranumBits = numBits % NetUtils.NumBitsInAByte; + int RestBits = numBits % NetUtils.NumBitsInAByte; + int InputMSBbits = 0, InputLSBbits = 0; + int i; + + if (numBits == 0) { + return; + } + + if (extraOffsetBits == 0) { + if (extranumBits == 0) { + numBytes = numBits / NetUtils.NumBitsInAByte; + System.arraycopy(inputdata, 0, data, startByteOffset, numBytes); + } else { + System.arraycopy(inputdata, 0, data, startByteOffset, numBytes); + data[startByteOffset + numBytes] = (byte) (data[startByteOffset + + numBytes] | (inputdata[numBytes] & getMSBMask(extranumBits))); + } + } else { + for (i = 0; i < numBytes; i++) { + if (i != 0) + InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits)); + InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte + - extraOffsetBits))); + InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits + : InputMSBbits + 256; + data[startByteOffset + i] = (byte) (data[startByteOffset + i] + | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits)); + InputMSBbits = InputLSBbits = 0; + } + if (RestBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) { + if (numBytes != 0) + InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits)); + InputMSBbits = (byte) (inputdata[i] & (getMSBMask(RestBits))); + InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits + : InputMSBbits + 256; + data[startByteOffset + i] = (byte) ((data[startByteOffset + i]) + | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits)); + } else if (RestBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) { + if (numBytes != 0) + InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits)); + InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte + - extraOffsetBits))); + InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits + : InputMSBbits + 256; + data[startByteOffset + i] = (byte) (data[startByteOffset + i] + | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits)); + } else { + if (numBytes != 0) + InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits)); + InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte + - extraOffsetBits))); + InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits + : InputMSBbits + 256; + data[startByteOffset + i] = (byte) (data[startByteOffset + i] + | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits)); + + InputLSBbits = (inputdata[i] & (getLSBMask(RestBits + - (NetUtils.NumBitsInAByte - extraOffsetBits)) << (NetUtils.NumBitsInAByte - RestBits))); + data[startByteOffset + i + 1] = (byte) (data[startByteOffset + + i + 1] | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits))); + } + } + } + + /** + * Checks for overflow and underflow exceptions + * @param data + * @param startOffset + * @param numBits + * @throws PacketException when the startOffset and numBits parameters + * are not congruent with the data buffer's size + */ + public static void checkExceptions(byte[] data, int startOffset, int numBits) + throws BufferException { + int endOffsetByte; + int startByteOffset; + endOffsetByte = startOffset + / NetUtils.NumBitsInAByte + + numBits + / NetUtils.NumBitsInAByte + + ((numBits % NetUtils.NumBitsInAByte != 0) ? 1 : ((startOffset + % NetUtils.NumBitsInAByte != 0) ? 1 : 0)); + startByteOffset = startOffset / NetUtils.NumBitsInAByte; + + if (data == null) { + throw new BufferException("data[] is null\n"); + } + + if ((startOffset < 0) || (startByteOffset >= data.length) + || (endOffsetByte > data.length) || (numBits < 0) + || (numBits > NetUtils.NumBitsInAByte * data.length)) { + throw new BufferException( + "Illegal arguement/out of bound exception - data.length = " + + data.length + " startOffset = " + startOffset + + " numBits " + numBits); + } + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/BufferException.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/BufferException.java new file mode 100644 index 0000000000..fa0848d894 --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/BufferException.java @@ -0,0 +1,19 @@ +/* + * 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.liblldp; + +/** + * Describes an exception that is raised during BitBufferHelper operations. + */ +public class BufferException extends Exception { + private static final long serialVersionUID = 1L; + + public BufferException(String message) { + super(message); + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/ConstructionException.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/ConstructionException.java new file mode 100644 index 0000000000..8b1d9d2d0f --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/ConstructionException.java @@ -0,0 +1,28 @@ + +/* + * 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 + */ + +/** + * @file ConstructionException.java + * + * + * @brief Describe an exception that is raised when a construction + * for a Node/NodeConnector/Edge or any of the SAL basic object fails + * because input passed are not valid or compatible + * + * + */ +package org.opendaylight.controller.liblldp; + +public class ConstructionException extends Exception { + private static final long serialVersionUID = 1L; + + public ConstructionException(String message) { + super(message); + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/DataLinkAddress.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/DataLinkAddress.java new file mode 100644 index 0000000000..d617c05a5a --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/DataLinkAddress.java @@ -0,0 +1,96 @@ + +/* + * 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.liblldp; + +import java.io.Serializable; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * @file DataLinkAddress.java + * + * @brief Abstract base class for a Datalink Address + * + */ + +/** + * Abstract base class for a Datalink Address + * + */ +@XmlRootElement +abstract public class DataLinkAddress implements Serializable { + private static final long serialVersionUID = 1L; + private String name; + + public DataLinkAddress() { + + } + + /** + * Constructor of super class + * + * @param name Create a new DataLink, not for general use but + * available only for sub classes + * + * @return constructed object + */ + protected DataLinkAddress(String name) { + this.name = name; + } + + /** + * Used to copy the DataLinkAddress in a polymorphic way + * + * + * @return A clone of this DataLinkAddress + */ + @Override + abstract public DataLinkAddress clone(); + + /** + * Allow to distinguish among different data link addresses + * + * + * @return Name of the DataLinkAdress we are working on + */ + public String getName() { + return this.name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DataLinkAddress other = (DataLinkAddress) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + + @Override + public String toString() { + return "DataLinkAddress [name=" + name + "]"; + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/EtherTypes.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/EtherTypes.java new file mode 100644 index 0000000000..876d495899 --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/EtherTypes.java @@ -0,0 +1,117 @@ + +/* + * 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.liblldp; + +import java.util.ArrayList; +import java.util.List; + +/** + * The enum contains the most common 802.3 ethernet types and 802.2 + SNAP protocol ids + * + * + * + */ +public enum EtherTypes { + PVSTP("PVSTP", 0x010B), // 802.2 + SNAP (Spanning Tree) + CDP("CDP", 0x2000), // 802.2 + SNAP + VTP("VTP", 0x2003), // 802.2 + SNAP + IPv4("IPv4", 0x800), ARP("ARP", 0x806), RARP("Reverse ARP", 0x8035), VLANTAGGED( + "VLAN Tagged", 0x8100), // 802.1Q + IPv6("IPv6", 0x86DD), MPLSUCAST("MPLS Unicast", 0x8847), MPLSMCAST( + "MPLS Multicast", 0x8848), QINQ("QINQ", 0x88A8), // Standard 802.1ad QinQ + LLDP("LLDP", 0x88CC), OLDQINQ("Old QINQ", 0x9100), // Old non-standard QinQ + CISCOQINQ("Cisco QINQ", 0x9200); // Cisco non-standard QinQ + + private static final String regexNumberString = "^[0-9]+$"; + private String description; + private int number; + + private EtherTypes(String description, int number) { + this.description = description; + this.number = number; + } + + public String toString() { + return description; + } + + public int intValue() { + return number; + } + + public short shortValue() { + return ((Integer) number).shortValue(); + } + + public static String getEtherTypeName(int number) { + return getEtherTypeInternal(number); + } + + public static String getEtherTypeName(short number) { + return getEtherTypeInternal((int) number & 0xffff); + } + + public static String getEtherTypeName(byte number) { + return getEtherTypeInternal((int) number & 0xff); + } + + private static String getEtherTypeInternal(int number) { + for (EtherTypes type : EtherTypes.values()) { + if (type.number == number) { + return type.toString(); + } + } + return "0x" + Integer.toHexString(number); + } + + public static short getEtherTypeNumberShort(String name) { + if (name.matches(regexNumberString)) { + return Short.valueOf(name); + } + for (EtherTypes type : EtherTypes.values()) { + if (type.description.equalsIgnoreCase(name)) { + return type.shortValue(); + } + } + return 0; + } + + public static int getEtherTypeNumberInt(String name) { + if (name.matches(regexNumberString)) { + return Integer.valueOf(name); + } + for (EtherTypes type : EtherTypes.values()) { + if (type.description.equalsIgnoreCase(name)) { + return type.intValue(); + } + } + return 0; + } + + public static List getEtherTypesNameList() { + List ethertypesList = new ArrayList(); + for (EtherTypes type : EtherTypes.values()) { + ethertypesList.add(type.toString()); + } + return ethertypesList; + } + + public static EtherTypes loadFromString(String string) { + int intType = Integer.parseInt(string); + + for (EtherTypes type : EtherTypes.values()) { + if (type.number == intType) { + return type; + } + } + return null; + } + +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/Ethernet.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/Ethernet.java new file mode 100644 index 0000000000..54452bb9a4 --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/Ethernet.java @@ -0,0 +1,134 @@ + +/* + * 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.liblldp; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Class that represents the Ethernet frame objects + */ +public class Ethernet extends Packet { + private static final String DMAC = "DestinationMACAddress"; + private static final String SMAC = "SourceMACAddress"; + private static final String ETHT = "EtherType"; + + // TODO: This has to be outside and it should be possible for osgi + // to add new coming packet classes + public static final Map> etherTypeClassMap; + static { + etherTypeClassMap = new HashMap>(); + etherTypeClassMap.put(EtherTypes.LLDP.shortValue(), LLDP.class); + } + private static Map> fieldCoordinates = new LinkedHashMap>() { + private static final long serialVersionUID = 1L; + { + put(DMAC, new ImmutablePair(0, 48)); + put(SMAC, new ImmutablePair(48, 48)); + put(ETHT, new ImmutablePair(96, 16)); + } + }; + private final Map fieldValues; + + /** + * Default constructor that creates and sets the HashMap + */ + public Ethernet() { + super(); + fieldValues = new HashMap(); + hdrFieldCoordMap = fieldCoordinates; + hdrFieldsMap = fieldValues; + } + + /** + * Constructor that sets the access level for the packet and + * creates and sets the HashMap + */ + public Ethernet(boolean writeAccess) { + super(writeAccess); + fieldValues = new HashMap(); + hdrFieldCoordMap = fieldCoordinates; + hdrFieldsMap = fieldValues; + } + + @Override + public void setHeaderField(String headerField, byte[] readValue) { + if (headerField.equals(ETHT)) { + payloadClass = etherTypeClassMap.get(BitBufferHelper + .getShort(readValue)); + } + hdrFieldsMap.put(headerField, readValue); + } + + /** + * Gets the destination MAC address stored + * @return byte[] - the destinationMACAddress + */ + public byte[] getDestinationMACAddress() { + return fieldValues.get(DMAC); + } + + /** + * Gets the source MAC address stored + * @return byte[] - the sourceMACAddress + */ + public byte[] getSourceMACAddress() { + return fieldValues.get(SMAC); + } + + /** + * Gets the etherType stored + * @return short - the etherType + */ + public short getEtherType() { + return BitBufferHelper.getShort(fieldValues.get(ETHT)); + } + + public boolean isBroadcast(){ + return NetUtils.isBroadcastMACAddr(getDestinationMACAddress()); + } + + public boolean isMulticast(){ + return NetUtils.isMulticastMACAddr(getDestinationMACAddress()); + } + + /** + * Sets the destination MAC address for the current Ethernet object instance + * @param byte[] - the destinationMACAddress to set + */ + public Ethernet setDestinationMACAddress(byte[] destinationMACAddress) { + fieldValues.put(DMAC, destinationMACAddress); + return this; + } + + /** + * Sets the source MAC address for the current Ethernet object instance + * @param byte[] - the sourceMACAddress to set + */ + public Ethernet setSourceMACAddress(byte[] sourceMACAddress) { + fieldValues.put(SMAC, sourceMACAddress); + return this; + } + + /** + * Sets the etherType for the current Ethernet object instance + * @param short - the etherType to set + */ + public Ethernet setEtherType(short etherType) { + byte[] ethType = BitBufferHelper.toByteArray(etherType); + fieldValues.put(ETHT, ethType); + return this; + } + +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/EthernetAddress.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/EthernetAddress.java new file mode 100644 index 0000000000..b7b72cbffd --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/EthernetAddress.java @@ -0,0 +1,124 @@ + +/* + * 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.liblldp; + +import java.util.Arrays; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class EthernetAddress extends DataLinkAddress { + private static final long serialVersionUID = 1L; + @XmlTransient + private byte[] macAddress; + + public static final EthernetAddress BROADCASTMAC = createWellKnownAddress(new byte[] { + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff }); + + public static final EthernetAddress INVALIDHOST = BROADCASTMAC; + + public static final String addressName = "Ethernet MAC Address"; + public static final int SIZE = 6; + + private static final EthernetAddress createWellKnownAddress(byte[] mac) { + try { + return new EthernetAddress(mac); + } catch (ConstructionException ce) { + return null; + } + } + + /* Private constructor to satisfy JAXB */ + @SuppressWarnings("unused") + private EthernetAddress() { + } + + /** + * Public constructor for an Ethernet MAC address starting from + * the byte constituing the address, the constructor validate the + * size of the arrive to make sure it met the expected size + * + * @param macAddress A byte array in big endian format + * representing the Ethernet MAC Address + * + * @return The constructed object if valid + */ + public EthernetAddress(byte[] macAddress) throws ConstructionException { + super(addressName); + + if (macAddress == null) { + throw new ConstructionException("Null input parameter passed"); + } + + if (macAddress.length != SIZE) { + throw new ConstructionException( + "Wrong size of passed byte array, expected:" + SIZE + + " got:" + macAddress.length); + } + this.macAddress = new byte[SIZE]; + System.arraycopy(macAddress, 0, this.macAddress, 0, SIZE); + } + + public EthernetAddress clone() { + try { + return new EthernetAddress(this.macAddress.clone()); + } catch (ConstructionException ce) { + return null; + } + } + + /** + * Return the Ethernet Mac address in byte array format + * + * @return The Ethernet Mac address in byte array format + */ + public byte[] getValue() { + return this.macAddress; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(macAddress); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + EthernetAddress other = (EthernetAddress) obj; + if (!Arrays.equals(macAddress, other.macAddress)) + return false; + return true; + } + + @Override + public String toString() { + return "EthernetAddress [macAddress=" + HexEncode.bytesToHexStringFormat(macAddress) + + "]"; + } + + @XmlElement(name = "macAddress") + public String getMacAddress() { + return HexEncode.bytesToHexStringFormat(macAddress); + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/HexEncode.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/HexEncode.java new file mode 100644 index 0000000000..8236d4c3c9 --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/HexEncode.java @@ -0,0 +1,114 @@ + +/* + * 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.liblldp; + +import java.math.BigInteger; + +/** + * The class provides methods to convert hex encode strings + * + * + */ +public class HexEncode { + /** + * This method converts byte array into String format without ":" inserted. + * + * @param bytes + * The byte array to convert to string + * @return The hexadecimal representation of the byte array. If bytes is + * null, "null" string is returned + */ + public static String bytesToHexString(byte[] bytes) { + + if (bytes == null) { + return "null"; + } + + String ret = ""; + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < bytes.length; i++) { + if (i > 0) { + ret += ":"; + } + short u8byte = (short) (bytes[i] & 0xff); + String tmp = Integer.toHexString(u8byte); + if (tmp.length() == 1) { + buf.append("0"); + } + buf.append(tmp); + } + ret = buf.toString(); + return ret; + } + + public static String longToHexString(long val) { + char arr[] = Long.toHexString(val).toCharArray(); + StringBuffer buf = new StringBuffer(); + // prepend the right number of leading zeros + int i = 0; + for (; i < (16 - arr.length); i++) { + buf.append("0"); + if ((i & 0x01) == 1) { + buf.append(":"); + } + } + for (int j = 0; j < arr.length; j++) { + buf.append(arr[j]); + if ((((i + j) & 0x01) == 1) && (j < (arr.length - 1))) { + buf.append(":"); + } + } + return buf.toString(); + } + + + public static byte[] bytesFromHexString(String values) { + String target = ""; + if (values != null) { + target = values; + } + String[] octets = target.split(":"); + + byte[] ret = new byte[octets.length]; + for (int i = 0; i < octets.length; i++) { + ret[i] = Integer.valueOf(octets[i], 16).byteValue(); + } + return ret; + } + + public static long stringToLong(String values) { + long value = new BigInteger(values.replaceAll(":", ""), 16).longValue(); + return value; + } + + /** + * This method converts byte array into HexString format with ":" inserted. + */ + public static String bytesToHexStringFormat(byte[] bytes) { + if (bytes == null) { + return "null"; + } + String ret = ""; + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < bytes.length; i++) { + if (i > 0) { + buf.append(":"); + } + short u8byte = (short) (bytes[i] & 0xff); + String tmp = Integer.toHexString(u8byte); + if (tmp.length() == 1) { + buf.append("0"); + } + buf.append(tmp); + } + ret = buf.toString(); + return ret; + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/LLDP.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/LLDP.java new file mode 100644 index 0000000000..9b7efbb1e6 --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/LLDP.java @@ -0,0 +1,259 @@ +/* + * 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.liblldp; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Class that represents the LLDP frame objects + */ + +public class LLDP extends Packet { + private static final String CHASSISID = "ChassisId"; + private static final String SYSTEMNAMEID = "SystemNameID"; + private static final String PORTID = "PortId"; + private static final String TTL = "TTL"; + private static final int LLDPDefaultTlvs = 4; + private static LLDPTLV emptyTLV = new LLDPTLV().setLength((short) 0) + .setType((byte) 0); + public static final byte[] LLDPMulticastMac = { 1, (byte) 0x80, + (byte) 0xc2, 0, 0, (byte) 0xe }; + private Map tlvList; + + /** + * Default constructor that creates the tlvList LinkedHashMap + */ + public LLDP() { + super(); + tlvList = new LinkedHashMap(LLDPDefaultTlvs); + } + + /** + * Constructor that creates the tlvList LinkedHashMap and sets the write + * access for the same + */ + public LLDP(boolean writeAccess) { + super(writeAccess); + tlvList = new LinkedHashMap(LLDPDefaultTlvs); // Mandatory + // TLVs + } + + /** + * @param String + * - description of the type of TLV + * @return byte - type of TLV + */ + private byte getType(String typeDesc) { + if (typeDesc.equals(CHASSISID)) { + return LLDPTLV.TLVType.ChassisID.getValue(); + } else if (typeDesc.equals(PORTID)) { + return LLDPTLV.TLVType.PortID.getValue(); + } else if (typeDesc.equals(TTL)) { + return LLDPTLV.TLVType.TTL.getValue(); + } else { + return LLDPTLV.TLVType.Unknown.getValue(); + } + } + + /** + * @param String + * - description of the type of TLV + * @return LLDPTLV - full TLV + */ + public LLDPTLV getTLV(String type) { + return tlvList.get(getType(type)); + } + + /** + * @param String + * - description of the type of TLV + * @param LLDPTLV + * - tlv to set + * @return void + */ + public void setTLV(String type, LLDPTLV tlv) { + tlvList.put(getType(type), tlv); + } + + /** + * @return the chassisId TLV + */ + public LLDPTLV getChassisId() { + return getTLV(CHASSISID); + } + + /** + * @param LLDPTLV + * - the chassisId to set + */ + public LLDP setChassisId(LLDPTLV chassisId) { + tlvList.put(getType(CHASSISID), chassisId); + return this; + } + + /** + * @return the SystemName TLV + */ + public LLDPTLV getSystemNameId() { + return getTLV(SYSTEMNAMEID); + } + + /** + * @param LLDPTLV + * - the chassisId to set + */ + public LLDP setSystemNameId(LLDPTLV systemNameId) { + tlvList.put(getType(SYSTEMNAMEID), systemNameId); + return this; + } + + /** + * @return LLDPTLV - the portId TLV + */ + public LLDPTLV getPortId() { + return tlvList.get(getType(PORTID)); + } + + /** + * @param LLDPTLV + * - the portId to set + * @return LLDP + */ + public LLDP setPortId(LLDPTLV portId) { + tlvList.put(getType(PORTID), portId); + return this; + } + + /** + * @return LLDPTLV - the ttl TLV + */ + public LLDPTLV getTtl() { + return tlvList.get(getType(TTL)); + } + + /** + * @param LLDPTLV + * - the ttl to set + * @return LLDP + */ + public LLDP setTtl(LLDPTLV ttl) { + tlvList.put(getType(TTL), ttl); + return this; + } + + /** + * @return the optionalTLVList + */ + public List getOptionalTLVList() { + List list = new ArrayList(); + for (Map.Entry entry : tlvList.entrySet()) { + byte type = entry.getKey(); + if ((type == LLDPTLV.TLVType.ChassisID.getValue()) + || (type == LLDPTLV.TLVType.PortID.getValue()) + || (type == LLDPTLV.TLVType.TTL.getValue())) { + continue; + } else { + list.add(entry.getValue()); + } + } + return list; + } + + /** + * @param optionalTLVList + * the optionalTLVList to set + * @return LLDP + */ + public LLDP setOptionalTLVList(List optionalTLVList) { + for (LLDPTLV tlv : optionalTLVList) { + tlvList.put(tlv.getType(), tlv); + } + return this; + } + + @Override + public Packet deserialize(byte[] data, int bitOffset, int size) + throws PacketException { + int lldpOffset = bitOffset; // LLDP start + int lldpSize = size; // LLDP size + + if (logger.isTraceEnabled()) { + logger.trace("LLDP: {} (offset {} bitsize {})", new Object[] { + HexEncode.bytesToHexString(data), lldpOffset, lldpSize }); + } + /* + * Deserialize the TLVs until we reach the end of the packet + */ + while (lldpSize > 0) { + LLDPTLV tlv = new LLDPTLV(); + tlv.deserialize(data, lldpOffset, lldpSize); + if (tlv.getType() == 0 && tlv.getLength() == 0) { + break; + } + int tlvSize = tlv.getTLVSize(); // Size of current TLV in bits + lldpOffset += tlvSize; + lldpSize -= tlvSize; + this.tlvList.put(tlv.getType(), tlv); + } + return this; + } + + @Override + public byte[] serialize() throws PacketException { + int startOffset = 0; + byte[] serializedBytes = new byte[getLLDPPacketLength()]; + + for (Map.Entry entry : tlvList.entrySet()) { + LLDPTLV tlv = entry.getValue(); + int numBits = tlv.getTLVSize(); + try { + BitBufferHelper.setBytes(serializedBytes, tlv.serialize(), + startOffset, numBits); + } catch (BufferException e) { + throw new PacketException(e.getMessage()); + } + startOffset += numBits; + } + // Now add the empty LLDPTLV at the end + try { + BitBufferHelper.setBytes(serializedBytes, + LLDP.emptyTLV.serialize(), startOffset, + LLDP.emptyTLV.getTLVSize()); + } catch (BufferException e) { + throw new PacketException(e.getMessage()); + } + + if (logger.isTraceEnabled()) { + logger.trace("LLDP: serialized: {}", + HexEncode.bytesToHexString(serializedBytes)); + } + return serializedBytes; + } + + /** + * Returns the size of LLDP packet in bytes + * + * @return int - LLDP Packet size in bytes + */ + private int getLLDPPacketLength() { + int len = 0; + LLDPTLV tlv; + + for (Map.Entry entry : this.tlvList.entrySet()) { + tlv = entry.getValue(); + len += tlv.getTLVSize(); + } + len += LLDP.emptyTLV.getTLVSize(); + + return len / NetUtils.NumBitsInAByte; + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/LLDPTLV.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/LLDPTLV.java new file mode 100644 index 0000000000..22bd4626d1 --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/LLDPTLV.java @@ -0,0 +1,337 @@ +/* + * 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.liblldp; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Class that represents the LLDPTLV objects + */ + +public class LLDPTLV extends Packet { + private static final String TYPE = "Type"; + private static final String LENGTH = "Length"; + private static final String VALUE = "Value"; + private static final int LLDPTLVFields = 3; + public static final byte[] OFOUI = new byte[] { (byte) 0x00, (byte) 0x26, + (byte) 0xe1 }; // OpenFlow OUI + public static final byte[] customTlvSubType = new byte[] { 0 }; + public static final int customTlvOffset = OFOUI.length + + customTlvSubType.length; + public static final byte chassisIDSubType[] = new byte[] { 4 }; // MAC address for the system + public static final byte portIDSubType[] = new byte[] { 7 }; // locally assigned + + public enum TLVType { + Unknown((byte) 0), ChassisID((byte) 1), PortID((byte) 2), TTL((byte) 3), PortDesc( + (byte) 4), SystemName((byte) 5), SystemDesc((byte) 6), Custom( + (byte) 127); + + private byte value; + + private TLVType(byte value) { + this.value = value; + } + + public byte getValue() { + return value; + } + } + + private static Map> fieldCoordinates = new LinkedHashMap>() { + private static final long serialVersionUID = 1L; + + { + put(TYPE, new MutablePair(0, 7)); + put(LENGTH, new MutablePair(7, 9)); + put(VALUE, new MutablePair(16, 0)); + } + }; + + protected Map fieldValues; + + /** + * Default constructor that creates and sets the hash map values and sets + * the payload to null + */ + public LLDPTLV() { + payload = null; + fieldValues = new HashMap(LLDPTLVFields); + hdrFieldCoordMap = fieldCoordinates; + hdrFieldsMap = fieldValues; + } + + /** + * Constructor that writes the passed LLDPTLV values to the hdrFieldsMap + */ + public LLDPTLV(LLDPTLV other) { + for (Map.Entry entry : other.hdrFieldsMap.entrySet()) { + this.hdrFieldsMap.put(entry.getKey(), entry.getValue()); + } + } + + /** + * @return int - the length of TLV + */ + public int getLength() { + return (int) BitBufferHelper.toNumber(fieldValues.get(LENGTH), + fieldCoordinates.get(LENGTH).getRight().intValue()); + } + + /** + * @return byte - the type of TLV + */ + public byte getType() { + return BitBufferHelper.getByte(fieldValues.get(TYPE)); + } + + /** + * @return byte[] - the value field of TLV + */ + public byte[] getValue() { + return fieldValues.get(VALUE); + } + + /** + * @param byte - the type to set + * @return LLDPTLV + */ + public LLDPTLV setType(byte type) { + byte[] lldpTLVtype = { type }; + fieldValues.put(TYPE, lldpTLVtype); + return this; + } + + /** + * @param short - the length to set + * @return LLDPTLV + */ + public LLDPTLV setLength(short length) { + fieldValues.put(LENGTH, BitBufferHelper.toByteArray(length)); + return this; + } + + /** + * @param byte[] - the value to set + * @return LLDPTLV + */ + public LLDPTLV setValue(byte[] value) { + fieldValues.put(VALUE, value); + return this; + } + + @Override + public void setHeaderField(String headerField, byte[] readValue) { + hdrFieldsMap.put(headerField, readValue); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + + ((fieldValues == null) ? 0 : fieldValues.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LLDPTLV other = (LLDPTLV) obj; + if (fieldValues == null) { + if (other.fieldValues != null) { + return false; + } + } else if (!fieldValues.equals(other.fieldValues)) { + return false; + } + return true; + } + + @Override + public int getfieldnumBits(String fieldName) { + if (fieldName.equals(VALUE)) { + return (NetUtils.NumBitsInAByte * BitBufferHelper.getShort( + fieldValues.get(LENGTH), fieldCoordinates.get(LENGTH) + .getRight().intValue())); + } + return fieldCoordinates.get(fieldName).getRight(); + } + + /** + * Returns the size in bits of the whole TLV + * + * @return int - size in bits of full TLV + */ + public int getTLVSize() { + return (LLDPTLV.fieldCoordinates.get(TYPE).getRight() + // static + LLDPTLV.fieldCoordinates.get(LENGTH).getRight() + // static + getfieldnumBits(VALUE)); // variable + } + + /** + * Creates the SystemName TLV value + * + * @param nodeId + * node identifier string + * @return the SystemName TLV value in byte array + */ + static public byte[] createSystemNameTLVValue(String nodeId) { + byte[] nid = nodeId.getBytes(); + return nid; + } + + /** + * Creates the ChassisID TLV value including the subtype and ChassisID + * string + * + * @param nodeId + * node identifier string + * @return the ChassisID TLV value in byte array + */ + static public byte[] createChassisIDTLVValue(String nodeId) { + byte[] nid = HexEncode.bytesFromHexString(nodeId); + byte[] cid = new byte[6]; + int srcPos = 0, dstPos = 0; + + if (nid.length > cid.length) { + srcPos = nid.length - cid.length; + } else { + dstPos = cid.length - nid.length; + } + System.arraycopy(nid, srcPos, cid, dstPos, cid.length); + + byte[] cidValue = new byte[cid.length + chassisIDSubType.length]; + + System.arraycopy(chassisIDSubType, 0, cidValue, 0, + chassisIDSubType.length); + System.arraycopy(cid, 0, cidValue, chassisIDSubType.length, cid.length); + + return cidValue; + } + + /** + * Creates the PortID TLV value including the subtype and PortID string + * + * @param portId + * port identifier string + * @return the PortID TLV value in byte array + */ + static public byte[] createPortIDTLVValue(String portId) { + byte[] pid = portId.getBytes(Charset.defaultCharset()); + byte[] pidValue = new byte[pid.length + portIDSubType.length]; + + System.arraycopy(portIDSubType, 0, pidValue, 0, portIDSubType.length); + System.arraycopy(pid, 0, pidValue, portIDSubType.length, pid.length); + + return pidValue; + } + + /** + * Creates the custom TLV value including OUI, subtype and custom string + * + * @param portId + * port identifier string + * @return the custom TLV value in byte array + */ + static public byte[] createCustomTLVValue(String customString) { + byte[] customArray = customString.getBytes(Charset.defaultCharset()); + byte[] customValue = new byte[customTlvOffset + customArray.length]; + + System.arraycopy(OFOUI, 0, customValue, 0, OFOUI.length); + System.arraycopy(customTlvSubType, 0, customValue, OFOUI.length, + customTlvSubType.length); + System.arraycopy(customArray, 0, customValue, customTlvOffset, + customArray.length); + + return customValue; + } + + /** + * Retrieves the string from TLV value and returns it in HexString format + * + * @param tlvValue + * the TLV value + * @param tlvLen + * the TLV length + * @return the HexString + */ + static public String getHexStringValue(byte[] tlvValue, int tlvLen) { + byte[] cidBytes = new byte[tlvLen - chassisIDSubType.length]; + System.arraycopy(tlvValue, chassisIDSubType.length, cidBytes, 0, + cidBytes.length); + return HexEncode.bytesToHexStringFormat(cidBytes); + } + + /** + * Retrieves the string from TLV value + * + * @param tlvValue + * the TLV value + * @param tlvLen + * the TLV length + * @return the string + */ + static public String getStringValue(byte[] tlvValue, int tlvLen) { + byte[] pidSubType = new byte[portIDSubType.length]; + byte[] pidBytes = new byte[tlvLen - portIDSubType.length]; + System.arraycopy(tlvValue, 0, pidSubType, 0, + pidSubType.length); + System.arraycopy(tlvValue, portIDSubType.length, pidBytes, 0, + pidBytes.length); + if (pidSubType[0] == (byte) 0x3) { + return HexEncode.bytesToHexStringFormat(pidBytes); + } else { + return (new String(pidBytes, Charset.defaultCharset())); + } + } + + /** + * Retrieves the custom string from the Custom TLV value which includes OUI, + * subtype and custom string + * + * @param customTlvValue + * the custom TLV value + * @param customTlvLen + * the custom TLV length + * @return the custom string + */ + static public String getCustomString(byte[] customTlvValue, int customTlvLen) { + String customString = ""; + byte[] vendor = new byte[3]; + System.arraycopy(customTlvValue, 0, vendor, 0, vendor.length); + if (Arrays.equals(vendor, LLDPTLV.OFOUI)) { + int customArrayLength = customTlvLen - customTlvOffset; + byte[] customArray = new byte[customArrayLength]; + System.arraycopy(customTlvValue, customTlvOffset, customArray, 0, + customArrayLength); + try { + customString = new String(customArray, "UTF-8"); + } catch (UnsupportedEncodingException e) { + } + } + + return customString; + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/NetUtils.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/NetUtils.java new file mode 100644 index 0000000000..0320cf6b47 --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/NetUtils.java @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2013-2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.liblldp; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class containing the common utility functions needed for operating on + * networking data structures + */ +public abstract class NetUtils { + protected static final Logger logger = LoggerFactory.getLogger(NetUtils.class); + /** + * Constant holding the number of bits in a byte + */ + public static final int NumBitsInAByte = 8; + + /** + * Constant holding the number of bytes in MAC Address + */ + public static final int MACAddrLengthInBytes = 6; + + /** + * Constant holding the number of words in MAC Address + */ + public static final int MACAddrLengthInWords = 3; + + /** + * Constant holding the broadcast MAC address + */ + private static final byte[] BroadcastMACAddr = {-1, -1, -1, -1, -1, -1}; + + /** + * Converts a 4 bytes array into an integer number + * + * @param ba + * the 4 bytes long byte array + * @return the integer number + */ + public static int byteArray4ToInt(byte[] ba) { + if (ba == null || ba.length != 4) { + return 0; + } + return (0xff & ba[0]) << 24 | (0xff & ba[1]) << 16 | (0xff & ba[2]) << 8 | (0xff & ba[3]); + } + + /** + * Converts a 6 bytes array into a long number MAC addresses. + * + * @param ba + * The 6 bytes long byte array. + * @return The long number. + * Zero is returned if {@code ba} is {@code null} or + * the length of it is not six. + */ + public static long byteArray6ToLong(byte[] ba) { + if (ba == null || ba.length != MACAddrLengthInBytes) { + return 0L; + } + long num = 0L; + int i = 0; + do { + num <<= NumBitsInAByte; + num |= 0xff & ba[i]; + i++; + } while (i < MACAddrLengthInBytes); + return num; + } + + /** + * Converts a long number to a 6 bytes array for MAC addresses. + * + * @param addr + * The long number. + * @return The byte array. + */ + public static byte[] longToByteArray6(long addr){ + byte[] mac = new byte[MACAddrLengthInBytes]; + int i = MACAddrLengthInBytes - 1; + do { + mac[i] = (byte) addr; + addr >>>= NumBitsInAByte; + i--; + } while (i >= 0); + return mac; + } + + /** + * Converts an integer number into a 4 bytes array + * + * @param i + * the integer number + * @return the byte array + */ + public static byte[] intToByteArray4(int i) { + return new byte[] { (byte) ((i >> 24) & 0xff), (byte) ((i >> 16) & 0xff), (byte) ((i >> 8) & 0xff), + (byte) (i & 0xff) }; + } + + /** + * Converts an IP address passed as integer value into the respective + * InetAddress object + * + * @param address + * the IP address in integer form + * @return the IP address in InetAddress form + */ + public static InetAddress getInetAddress(int address) { + InetAddress ip = null; + try { + ip = InetAddress.getByAddress(NetUtils.intToByteArray4(address)); + } catch (UnknownHostException e) { + logger.error("", e); + } + return ip; + } + + /** + * Return the InetAddress Network Mask given the length of the prefix bit + * mask. The prefix bit mask indicates the contiguous leading bits that are + * NOT masked out. Example: A prefix bit mask length of 8 will give an + * InetAddress Network Mask of 255.0.0.0 + * + * @param prefixMaskLength + * integer representing the length of the prefix network mask + * @param isV6 + * boolean representing the IP version of the returned address + * @return + */ + public static InetAddress getInetNetworkMask(int prefixMaskLength, boolean isV6) { + if (prefixMaskLength < 0 || (!isV6 && prefixMaskLength > 32) || (isV6 && prefixMaskLength > 128)) { + return null; + } + byte v4Address[] = { 0, 0, 0, 0 }; + byte v6Address[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + byte address[] = (isV6) ? v6Address : v4Address; + int numBytes = prefixMaskLength / 8; + int numBits = prefixMaskLength % 8; + int i = 0; + for (; i < numBytes; i++) { + address[i] = (byte) 0xff; + } + if (numBits > 0) { + int rem = 0; + for (int j = 0; j < numBits; j++) { + rem |= 1 << (7 - j); + } + address[i] = (byte) rem; + } + + try { + return InetAddress.getByAddress(address); + } catch (UnknownHostException e) { + logger.error("", e); + } + return null; + } + + /** + * Returns the prefix size in bits of the specified subnet mask. Example: + * For the subnet mask ff.ff.ff.e0 it returns 25 while for ff.00.00.00 it + * returns 8. If the passed subnetMask array is null, 0 is returned. + * + * @param subnetMask + * the subnet mask as byte array + * @return the prefix length as number of bits + */ + public static int getSubnetMaskLength(byte[] subnetMask) { + int maskLength = 0; + if (subnetMask != null && (subnetMask.length == 4 || subnetMask.length == 16)) { + int index = 0; + while (index < subnetMask.length && subnetMask[index] == (byte) 0xFF) { + maskLength += NetUtils.NumBitsInAByte; + index++; + } + if (index != subnetMask.length) { + int bits = NetUtils.NumBitsInAByte - 1; + while (bits >= 0 && (subnetMask[index] & 1 << bits) != 0) { + bits--; + maskLength++; + } + } + } + return maskLength; + } + + /** + * Returns the prefix size in bits of the specified subnet mask. Example: + * For the subnet mask 255.255.255.128 it returns 25 while for 255.0.0.0 it + * returns 8. If the passed subnetMask object is null, 0 is returned + * + * @param subnetMask + * the subnet mask as InetAddress + * @return the prefix length as number of bits + */ + public static int getSubnetMaskLength(InetAddress subnetMask) { + return subnetMask == null ? 0 : NetUtils.getSubnetMaskLength(subnetMask.getAddress()); + } + + /** + * Given an IP address and a prefix network mask length, it returns the + * equivalent subnet prefix IP address Example: for ip = "172.28.30.254" and + * maskLen = 25 it will return "172.28.30.128" + * + * @param ip + * the IP address in InetAddress form + * @param maskLen + * the length of the prefix network mask + * @return the subnet prefix IP address in InetAddress form + */ + public static InetAddress getSubnetPrefix(InetAddress ip, int maskLen) { + int bytes = maskLen / 8; + int bits = maskLen % 8; + byte modifiedByte; + byte[] sn = ip.getAddress(); + if (bits > 0) { + modifiedByte = (byte) (sn[bytes] >> (8 - bits)); + sn[bytes] = (byte) (modifiedByte << (8 - bits)); + bytes++; + } + for (; bytes < sn.length; bytes++) { + sn[bytes] = (byte) (0); + } + try { + return InetAddress.getByAddress(sn); + } catch (UnknownHostException e) { + return null; + } + } + + /** + * Checks if the test address and mask conflicts with the filter address and + * mask + * + * For example: + * testAddress: 172.28.2.23 + * testMask: 255.255.255.0 + * filterAddress: 172.28.1.10 + * testMask: 255.255.255.0 + * do conflict + * + * testAddress: 172.28.2.23 + * testMask: 255.255.255.0 + * filterAddress: 172.28.1.10 + * testMask: 255.255.0.0 + * do not conflict + * + * Null parameters are permitted + * + * @param testAddress + * @param filterAddress + * @param testMask + * @param filterMask + * @return + */ + public static boolean inetAddressConflict(InetAddress testAddress, InetAddress filterAddress, InetAddress testMask, + InetAddress filterMask) { + // Sanity check + if ((testAddress == null) || (filterAddress == null)) { + return false; + } + + // Presence check + if (isAny(testAddress) || isAny(filterAddress)) { + return false; + } + + int testMaskLen = (testMask == null) ? ((testAddress instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(testMask); + int filterMaskLen = (filterMask == null) ? ((testAddress instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(filterMask); + + // Mask length check. Test mask has to be more specific than filter one + if (testMaskLen < filterMaskLen) { + return true; + } + + // Subnet Prefix on filter mask length must be the same + InetAddress prefix1 = getSubnetPrefix(testAddress, filterMaskLen); + InetAddress prefix2 = getSubnetPrefix(filterAddress, filterMaskLen); + return (!prefix1.equals(prefix2)); + } + + /** + * Returns true if the passed MAC address is all zero + * + * @param mac + * the byte array representing the MAC address + * @return true if all MAC bytes are zero + */ + public static boolean isZeroMAC(byte[] mac) { + for (short i = 0; i < 6; i++) { + if (mac[i] != 0) { + return false; + } + } + return true; + } + + /** + * Returns true if the MAC address is the broadcast MAC address and false + * otherwise. + * + * @param MACAddress + * @return + */ + public static boolean isBroadcastMACAddr(byte[] MACAddress) { + if (MACAddress.length == MACAddrLengthInBytes) { + for (int i = 0; i < 6; i++) { + if (MACAddress[i] != BroadcastMACAddr[i]) { + return false; + } + } + return true; + } + + return false; + } + /** + * Returns true if the MAC address is a unicast MAC address and false + * otherwise. + * + * @param MACAddress + * @return + */ + public static boolean isUnicastMACAddr(byte[] MACAddress) { + if (MACAddress.length == MACAddrLengthInBytes) { + return (MACAddress[0] & 1) == 0; + } + return false; + } + + /** + * Returns true if the MAC address is a multicast MAC address and false + * otherwise. Note that this explicitly returns false for the broadcast MAC + * address. + * + * @param MACAddress + * @return + */ + public static boolean isMulticastMACAddr(byte[] MACAddress) { + if (MACAddress.length == MACAddrLengthInBytes && !isBroadcastMACAddr(MACAddress)) { + return (MACAddress[0] & 1) != 0; + } + return false; + } + + /** + * Returns true if the passed InetAddress contains all zero + * + * @param ip + * the IP address to test + * @return true if the address is all zero + */ + public static boolean isAny(InetAddress ip) { + for (byte b : ip.getAddress()) { + if (b != 0) { + return false; + } + } + return true; + } + + public static boolean fieldsConflict(int field1, int field2) { + if ((field1 == 0) || (field2 == 0) || (field1 == field2)) { + return false; + } + return true; + } + + public static InetAddress parseInetAddress(String addressString) { + InetAddress address = null; + try { + address = InetAddress.getByName(addressString); + } catch (UnknownHostException e) { + logger.error("", e); + } + return address; + } + + /** + * Checks if the passed IP v4 address in string form is valid The address + * may specify a mask at the end as "/MM" + * + * @param cidr + * the v4 address as A.B.C.D/MM + * @return + */ + public static boolean isIPv4AddressValid(String cidr) { + if (cidr == null) { + return false; + } + + String values[] = cidr.split("/"); + Pattern ipv4Pattern = Pattern + .compile("(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])"); + Matcher mm = ipv4Pattern.matcher(values[0]); + if (!mm.matches()) { + return false; + } + if (values.length >= 2) { + int prefix = Integer.valueOf(values[1]); + if ((prefix < 0) || (prefix > 32)) { + return false; + } + } + return true; + } + + /** + * Checks if the passed IP v6 address in string form is valid The address + * may specify a mask at the end as "/MMM" + * + * @param cidr + * the v6 address as A::1/MMM + * @return + */ + public static boolean isIPv6AddressValid(String cidr) { + if (cidr == null) { + return false; + } + + String values[] = cidr.split("/"); + try { + // when given an IP address, InetAddress.getByName validates the ip + // address + InetAddress addr = InetAddress.getByName(values[0]); + if (!(addr instanceof Inet6Address)) { + return false; + } + } catch (UnknownHostException ex) { + return false; + } + + if (values.length >= 2) { + int prefix = Integer.valueOf(values[1]); + if ((prefix < 0) || (prefix > 128)) { + return false; + } + } + return true; + } + + /** + * Checks if the passed IP address in string form is a valid v4 or v6 + * address. The address may specify a mask at the end as "/MMM" + * + * @param cidr + * the v4 or v6 address as IP/MMM + * @return + */ + public static boolean isIPAddressValid(String cidr) { + return NetUtils.isIPv4AddressValid(cidr) || NetUtils.isIPv6AddressValid(cidr); + } + + /* + * Following utilities are useful when you need to compare or bit shift java + * primitive type variable which are inherently signed + */ + /** + * Returns the unsigned value of the passed byte variable + * + * @param b + * the byte value + * @return the int variable containing the unsigned byte value + */ + public static int getUnsignedByte(byte b) { + return b & 0xFF; + } + + /** + * Return the unsigned value of the passed short variable + * + * @param s + * the short value + * @return the int variable containing the unsigned short value + */ + public static int getUnsignedShort(short s) { + return s & 0xFFFF; + } + + /** + * Returns the highest v4 or v6 InetAddress + * + * @param v6 + * true for IPv6, false for Ipv4 + * @return The highest IPv4 or IPv6 address + */ + public static InetAddress gethighestIP(boolean v6) { + try { + return (v6) ? InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") : InetAddress + .getByName("255.255.255.255"); + } catch (UnknownHostException e) { + return null; + } + } + + /** + * Returns Broadcast MAC Address + * + * @return the byte array containing broadcast mac address + */ + public static byte[] getBroadcastMACAddr() { + return Arrays.copyOf(BroadcastMACAddr, BroadcastMACAddr.length); + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/Packet.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/Packet.java new file mode 100644 index 0000000000..2af185221c --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/Packet.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2013-2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.liblldp; + +import java.util.Arrays; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang3.tuple.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract class which represents the generic network packet object It provides + * the basic methods which are common for all the packets, like serialize and + * deserialize + */ + +public abstract class Packet { + protected static final Logger logger = LoggerFactory + .getLogger(Packet.class); + // Access level granted to this packet + protected boolean writeAccess; + // When deserialized from wire, packet could result corrupted + protected boolean corrupted; + // The packet that encapsulate this packet + protected Packet parent; + // The packet encapsulated by this packet + protected Packet payload; + // The unparsed raw payload carried by this packet + protected byte[] rawPayload; + // Bit coordinates of packet header fields + protected Map> hdrFieldCoordMap; + // Header fields values: Map + protected Map hdrFieldsMap; + // The class of the encapsulated packet object + protected Class payloadClass; + + public Packet() { + writeAccess = false; + corrupted = false; + } + + public Packet(boolean writeAccess) { + this.writeAccess = writeAccess; + corrupted = false; + } + + public Packet getParent() { + return parent; + } + + public Packet getPayload() { + return payload; + } + + public void setParent(Packet parent) { + this.parent = parent; + } + + public void setPayload(Packet payload) { + this.payload = payload; + } + + public void setHeaderField(String headerField, byte[] readValue) { + hdrFieldsMap.put(headerField, readValue); + } + + /** + * This method deserializes the data bits obtained from the wire into the + * respective header and payload which are of type Packet + * + * @param byte[] data - data from wire to deserialize + * @param int bitOffset bit position where packet header starts in data + * array + * @param int size of packet in bits + * @return Packet + * @throws PacketException + */ + public Packet deserialize(byte[] data, int bitOffset, int size) + throws PacketException { + + // Deserialize the header fields one by one + int startOffset = 0, numBits = 0; + for (Entry> pairs : hdrFieldCoordMap + .entrySet()) { + String hdrField = pairs.getKey(); + startOffset = bitOffset + this.getfieldOffset(hdrField); + numBits = this.getfieldnumBits(hdrField); + + byte[] hdrFieldBytes = null; + try { + hdrFieldBytes = BitBufferHelper.getBits(data, startOffset, + numBits); + } catch (BufferException e) { + throw new PacketException(e.getMessage()); + } + + /* + * Store the raw read value, checks the payload type and set the + * payloadClass accordingly + */ + this.setHeaderField(hdrField, hdrFieldBytes); + + if (logger.isTraceEnabled()) { + logger.trace("{}: {}: {} (offset {} bitsize {})", + new Object[] { this.getClass().getSimpleName(), hdrField, + HexEncode.bytesToHexString(hdrFieldBytes), + startOffset, numBits }); + } + } + + // Deserialize the payload now + int payloadStart = startOffset + numBits; + int payloadSize = data.length * NetUtils.NumBitsInAByte - payloadStart; + + if (payloadClass != null) { + try { + payload = payloadClass.newInstance(); + } catch (Exception e) { + throw new RuntimeException( + "Error parsing payload for Ethernet packet", e); + } + payload.deserialize(data, payloadStart, payloadSize); + payload.setParent(this); + } else { + /* + * The payload class was not set, it means no class for parsing + * this payload is present. Let's store the raw payload if any. + */ + int start = payloadStart / NetUtils.NumBitsInAByte; + int stop = start + payloadSize / NetUtils.NumBitsInAByte; + rawPayload = Arrays.copyOfRange(data, start, stop); + } + + + // Take care of computation that can be done only after deserialization + postDeserializeCustomOperation(data, payloadStart - getHeaderSize()); + + return this; + } + + /** + * This method serializes the header and payload from the respective + * packet class, into a single stream of bytes to be sent on the wire + * + * @return The byte array representing the serialized Packet + * @throws PacketException + */ + public byte[] serialize() throws PacketException { + + // Acquire or compute the serialized payload + byte[] payloadBytes = null; + if (payload != null) { + payloadBytes = payload.serialize(); + } else if (rawPayload != null) { + payloadBytes = rawPayload; + } + int payloadSize = (payloadBytes == null) ? 0 : payloadBytes.length; + + // Allocate the buffer to contain the full (header + payload) packet + int headerSize = this.getHeaderSize() / NetUtils.NumBitsInAByte; + byte packetBytes[] = new byte[headerSize + payloadSize]; + if (payloadBytes != null) { + System.arraycopy(payloadBytes, 0, packetBytes, headerSize, payloadSize); + } + + // Serialize this packet header, field by field + for (Map.Entry> pairs : hdrFieldCoordMap + .entrySet()) { + String field = pairs.getKey(); + byte[] fieldBytes = hdrFieldsMap.get(field); + // Let's skip optional fields when not set + if (fieldBytes != null) { + try { + BitBufferHelper.setBytes(packetBytes, fieldBytes, + getfieldOffset(field), getfieldnumBits(field)); + } catch (BufferException e) { + throw new PacketException(e.getMessage()); + } + } + } + + // Perform post serialize operations (like checksum computation) + postSerializeCustomOperation(packetBytes); + + if (logger.isTraceEnabled()) { + logger.trace("{}: {}", this.getClass().getSimpleName(), + HexEncode.bytesToHexString(packetBytes)); + } + + return packetBytes; + } + + /** + * This method gets called at the end of the serialization process It is + * intended for the child packets to insert some custom data into the output + * byte stream which cannot be done or cannot be done efficiently during the + * normal Packet.serialize() path. An example is the checksum computation + * for IPv4 + * + * @param byte[] - serialized bytes + * @throws PacketException + */ + protected void postSerializeCustomOperation(byte[] myBytes) + throws PacketException { + // no op + } + + /** + * This method re-computes the checksum of the bits received on the wire and + * validates it with the checksum in the bits received Since the computation + * of checksum varies based on the protocol, this method is overridden. + * Currently only IPv4 and ICMP do checksum computation and validation. TCP + * and UDP need to implement these if required. + * + * @param byte[] data The byte stream representing the Ethernet frame + * @param int startBitOffset The bit offset from where the byte array corresponding to this Packet starts in the frame + * @throws PacketException + */ + protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) + throws PacketException { + // no op + } + + /** + * Gets the header length in bits + * + * @return int the header length in bits + */ + public int getHeaderSize() { + int size = 0; + /* + * We need to iterate over the fields that were read in the frame + * (hdrFieldsMap) not all the possible ones described in + * hdrFieldCoordMap. For ex, 802.1Q may or may not be there + */ + for (Map.Entry fieldEntry : hdrFieldsMap.entrySet()) { + if (fieldEntry.getValue() != null) { + String field = fieldEntry.getKey(); + size += getfieldnumBits(field); + } + } + return size; + } + + /** + * This method fetches the start bit offset for header field specified by + * 'fieldname'. The offset is present in the hdrFieldCoordMap of the + * respective packet class + * + * @param String + * fieldName + * @return Integer - startOffset of the requested field + */ + public int getfieldOffset(String fieldName) { + return hdrFieldCoordMap.get(fieldName).getLeft(); + } + + /** + * This method fetches the number of bits for header field specified by + * 'fieldname'. The numBits are present in the hdrFieldCoordMap of the + * respective packet class + * + * @param String + * fieldName + * @return Integer - number of bits of the requested field + */ + public int getfieldnumBits(String fieldName) { + return hdrFieldCoordMap.get(fieldName).getRight(); + } + + @Override + public String toString() { + StringBuilder ret = new StringBuilder(); + ret.append(this.getClass().getSimpleName()); + ret.append(": ["); + for (String field : hdrFieldCoordMap.keySet()) { + byte[] value = hdrFieldsMap.get(field); + ret.append(field); + ret.append(": "); + ret.append(HexEncode.bytesToHexString(value)); + ret.append(", "); + } + ret.replace(ret.length()-2, ret.length()-1, "]"); + return ret.toString(); + } + + /** + * Returns the raw payload carried by this packet in case payload was not + * parsed. Caller can call this function in case the getPaylod() returns null. + * + * @return The raw payload if not parsable as an array of bytes, null otherwise + */ + public byte[] getRawPayload() { + return rawPayload; + } + + /** + * Set a raw payload in the packet class + * + * @param payload The raw payload as byte array + */ + public void setRawPayload(byte[] payload) { + this.rawPayload = Arrays.copyOf(payload, payload.length); + } + + /** + * Return whether the deserialized packet is to be considered corrupted. + * This is the case when the checksum computed after reconstructing the + * packet received from wire is not equal to the checksum read from the + * stream. For the Packet class which do not have a checksum field, this + * function will always return false. + * + * + * @return true if the deserialized packet's recomputed checksum is not + * equal to the packet carried checksum + */ + public boolean isCorrupted() { + return corrupted; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + + ((this.hdrFieldsMap == null) ? 0 : hdrFieldsMap.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (getClass() != obj.getClass()) { + return false; + } + Packet other = (Packet) obj; + if (hdrFieldsMap == other.hdrFieldsMap) { + return true; + } + if (hdrFieldsMap == null || other.hdrFieldsMap == null) { + return false; + } + if (hdrFieldsMap != null && other.hdrFieldsMap != null) { + for (String field : hdrFieldsMap.keySet()) { + if (!Arrays.equals(hdrFieldsMap.get(field), other.hdrFieldsMap.get(field))) { + return false; + } + } + } else { + return false; + } + return true; + } +} diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/PacketException.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/PacketException.java new file mode 100644 index 0000000000..c69fc03e91 --- /dev/null +++ b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/PacketException.java @@ -0,0 +1,22 @@ +/* + * 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.liblldp; + +/** + * Describes an exception that is raised when the process of serializing or + * deserializing a network packet/stream fails. This generally happens when the + * packet/stream is malformed. + * + */ +public class PacketException extends Exception { + private static final long serialVersionUID = 1L; + + public PacketException(String message) { + super(message); + } +} diff --git a/opendaylight/commons/liblldp/src/test/java/org/opendaylight/controller/sal/packet/BitBufferHelperTest.java b/opendaylight/commons/liblldp/src/test/java/org/opendaylight/controller/sal/packet/BitBufferHelperTest.java new file mode 100644 index 0000000000..07fbf0599b --- /dev/null +++ b/opendaylight/commons/liblldp/src/test/java/org/opendaylight/controller/sal/packet/BitBufferHelperTest.java @@ -0,0 +1,693 @@ + +/* + * 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.sal.packet; + +import junit.framework.Assert; + +import org.junit.Test; +import org.opendaylight.controller.liblldp.BitBufferHelper; + +public class BitBufferHelperTest { + + @Test + public void testGetByte() { + byte[] data = { 100 }; + Assert.assertTrue(BitBufferHelper.getByte(data) == 100); + } + + @Test + public void testGetBits() throws Exception { + byte[] data = { 10, 12, 14, 20, 55, 69, 82, 97, 109, 117, 127, -50 }; + byte[] bits; + + bits = BitBufferHelper.getBits(data, 88, 8); //BYTE extraOffsetBits = extranumBits = 0 + Assert.assertTrue(bits[0] == -50); + + bits = BitBufferHelper.getBits(data, 8, 16); //Short + Assert.assertTrue(bits[0] == 12); + Assert.assertTrue(bits[1] == 14); + + bits = BitBufferHelper.getBits(data, 32, 32); //Int + Assert.assertTrue(bits[0] == 55); + Assert.assertTrue(bits[1] == 69); + Assert.assertTrue(bits[2] == 82); + Assert.assertTrue(bits[3] == 97); + + bits = BitBufferHelper.getBits(data, 16, 48); //Long + Assert.assertTrue(bits[0] == 14); + Assert.assertTrue(bits[1] == 20); + Assert.assertTrue(bits[2] == 55); + Assert.assertTrue(bits[3] == 69); + Assert.assertTrue(bits[4] == 82); + Assert.assertTrue(bits[5] == 97); + + bits = BitBufferHelper.getBits(data, 40, 7); //BYTE extraOffsetBits = extranumBits != 0 + Assert.assertTrue(bits[0] == 34); + + bits = BitBufferHelper.getBits(data, 8, 13); //Short + Assert.assertTrue(bits[0] == 1); + Assert.assertTrue(bits[1] == -127); + + bits = BitBufferHelper.getBits(data, 32, 28); //Int + Assert.assertTrue(bits[0] == 3); + Assert.assertTrue(bits[1] == 116); + Assert.assertTrue(bits[2] == 85); + Assert.assertTrue(bits[3] == 38); + + bits = BitBufferHelper.getBits(data, 16, 41); //Long + Assert.assertTrue(bits[0] == 0); + Assert.assertTrue(bits[1] == 28); + Assert.assertTrue(bits[2] == 40); + Assert.assertTrue(bits[3] == 110); + Assert.assertTrue(bits[4] == -118); + Assert.assertTrue(bits[5] == -92); + + bits = BitBufferHelper.getBits(data, 3, 7); //BYTE extraOffsetBits != 0; extranumBits == 0 + Assert.assertTrue(bits[0] == 40); + + bits = BitBufferHelper.getBits(data, 13, 16); //Short + Assert.assertTrue(bits[0] == -127); + Assert.assertTrue(bits[1] == -62); + + bits = BitBufferHelper.getBits(data, 5, 32); //Int + Assert.assertTrue(bits[0] == 65); + Assert.assertTrue(bits[1] == -127); + Assert.assertTrue(bits[2] == -62); + Assert.assertTrue(bits[3] == -122); + + bits = BitBufferHelper.getBits(data, 23, 48); //Long + Assert.assertTrue(bits[0] == 10); + Assert.assertTrue(bits[1] == 27); + Assert.assertTrue(bits[2] == -94); + Assert.assertTrue(bits[3] == -87); + Assert.assertTrue(bits[4] == 48); + Assert.assertTrue(bits[5] == -74); + + bits = BitBufferHelper.getBits(data, 66, 9); //BYTE extraOffsetBits != 0; extranumBits != 0 + Assert.assertTrue(bits[0] == 1); + Assert.assertTrue(bits[1] == 107); + + bits = BitBufferHelper.getBits(data, 13, 15); //Short + Assert.assertTrue(bits[0] == 64); + Assert.assertTrue(bits[1] == -31); + + bits = BitBufferHelper.getBits(data, 5, 29); //Int + Assert.assertTrue(bits[0] == 8); + Assert.assertTrue(bits[1] == 48); + Assert.assertTrue(bits[2] == 56); + Assert.assertTrue(bits[3] == 80); + + bits = BitBufferHelper.getBits(data, 31, 43); //Long + Assert.assertTrue(bits[0] == 0); + Assert.assertTrue(bits[1] == -35); + Assert.assertTrue(bits[2] == 21); + Assert.assertTrue(bits[3] == 73); + Assert.assertTrue(bits[4] == -123); + Assert.assertTrue(bits[5] == -75); + + bits = BitBufferHelper.getBits(data, 4, 12); //Short + Assert.assertTrue(bits[0] == 10); + Assert.assertTrue(bits[1] == 12); + + byte[] data1 = { 0, 8 }; + bits = BitBufferHelper.getBits(data1, 7, 9); //Short + Assert.assertTrue(bits[0] == 0); + Assert.assertTrue(bits[1] == 8); + + byte[] data2 = { 2, 8 }; + bits = BitBufferHelper.getBits(data2, 0, 7); //Short + Assert.assertTrue(bits[0] == 1); + + bits = BitBufferHelper.getBits(data2, 7, 9); //Short + Assert.assertTrue(bits[0] == 0); + Assert.assertTrue(bits[1] == 8); + } + + // [01101100][01100000] + // [01100011] + @Test + public void testGetBytes() throws Exception { + byte data[] = { 108, 96, 125, -112, 5, 6, 108, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22 }; + byte[] x; + + Assert.assertTrue(BitBufferHelper.getBits(data, 0, 8)[0] == 108); + Assert.assertTrue(BitBufferHelper.getBits(data, 8, 8)[0] == 96); + + x = BitBufferHelper.getBits(data, 0, 10); + Assert.assertTrue(x[0] == 1); + Assert.assertTrue(x[1] == -79); + + x = BitBufferHelper.getBits(data, 3, 8); + Assert.assertTrue(x[0] == 99); + //Assert.assertTrue(x[1] == 97); + + } + + @Test + public void testMSBMask() { + int numBits = 1; //MSB + int mask = BitBufferHelper.getMSBMask(numBits); + Assert.assertTrue(mask == 128); + + numBits = 8; + mask = BitBufferHelper.getMSBMask(numBits); + Assert.assertTrue(mask == 255); + + numBits = 2; + mask = BitBufferHelper.getMSBMask(numBits); + Assert.assertTrue(mask == 192); + } + + @Test + public void testLSBMask() { + int numBits = 1; //LSB + int mask = BitBufferHelper.getLSBMask(numBits); + Assert.assertTrue(mask == 1); + + numBits = 3; + mask = BitBufferHelper.getLSBMask(numBits); + Assert.assertTrue(mask == 7); + + numBits = 8; + mask = BitBufferHelper.getLSBMask(numBits); + Assert.assertTrue(mask == 255); + } + + @Test + public void testToByteArray() { + short sh = Short.MAX_VALUE; + byte[] data_sh = new byte[Byte.SIZE / 8]; + data_sh = BitBufferHelper.toByteArray(sh); + Assert.assertTrue(data_sh[0] == 127); + Assert.assertTrue(data_sh[1] == -1); + + short sh2 = Short.MIN_VALUE; + byte[] data_sh2 = new byte[Byte.SIZE / 8]; + data_sh2 = BitBufferHelper.toByteArray(sh2); + Assert.assertTrue(data_sh2[0] == -128); + Assert.assertTrue(data_sh2[1] == 0); + + short sh3 = 16384; + byte[] data_sh3 = new byte[Byte.SIZE / 8]; + data_sh3 = BitBufferHelper.toByteArray(sh3); + Assert.assertTrue(data_sh3[0] == 64); + Assert.assertTrue(data_sh3[1] == 0); + + short sh4 = 146; //TCP headerlenflags - startoffset = 103 + byte[] data_sh4 = new byte[Byte.SIZE / 8]; + data_sh4 = BitBufferHelper.toByteArray(sh4); + Assert.assertTrue(data_sh4[0] == 0); + Assert.assertTrue(data_sh4[1] == -110); + + short sh4_2 = 5000; //IPv4 Offset - startOffset = 51 (to 63) + byte[] data_sh4_2 = new byte[Byte.SIZE / 8]; + data_sh4_2 = BitBufferHelper.toByteArray(sh4_2); + Assert.assertTrue(data_sh4_2[0] == 19); + Assert.assertTrue(data_sh4_2[1] == -120); + + short sh4_3 = 5312; //numEndRestBits < numBitstoShiftBy + byte[] data_sh4_3 = new byte[Byte.SIZE / 8]; + data_sh4_3 = BitBufferHelper.toByteArray(sh4_3); + Assert.assertTrue(data_sh4_3[0] == 20); + Assert.assertTrue(data_sh4_3[1] == -64); + + int Int = Integer.MAX_VALUE; + byte[] data_Int = new byte[Integer.SIZE / 8]; + data_Int = BitBufferHelper.toByteArray(Int); + Assert.assertTrue(data_Int[0] == 127); + Assert.assertTrue(data_Int[1] == -1); + Assert.assertTrue(data_Int[2] == -1); + Assert.assertTrue(data_Int[3] == -1); + + int Int2 = Integer.MIN_VALUE; + byte[] data_Int2 = new byte[Integer.SIZE / 8]; + data_Int2 = BitBufferHelper.toByteArray(Int2); + Assert.assertTrue(data_Int2[0] == -128); + Assert.assertTrue(data_Int2[1] == 0); + Assert.assertTrue(data_Int2[2] == 0); + Assert.assertTrue(data_Int2[3] == 0); + + int Int3 = 1077952576; + byte[] data_Int3 = new byte[Integer.SIZE / 8]; + data_Int3 = BitBufferHelper.toByteArray(Int3); + Assert.assertTrue(data_Int3[0] == 64); + Assert.assertTrue(data_Int3[1] == 64); + Assert.assertTrue(data_Int3[2] == 64); + Assert.assertTrue(data_Int3[3] == 64); + + long Lng = Long.MAX_VALUE; + byte[] data_lng = new byte[Long.SIZE / 8]; + data_lng = BitBufferHelper.toByteArray(Lng); + Assert.assertTrue(data_lng[0] == 127); + Assert.assertTrue(data_lng[1] == -1); + Assert.assertTrue(data_lng[2] == -1); + Assert.assertTrue(data_lng[3] == -1); + Assert.assertTrue(data_lng[4] == -1); + Assert.assertTrue(data_lng[5] == -1); + Assert.assertTrue(data_lng[6] == -1); + Assert.assertTrue(data_lng[7] == -1); + + long Lng2 = Long.MIN_VALUE; + byte[] data_lng2 = new byte[Long.SIZE / 8]; + data_lng2 = BitBufferHelper.toByteArray(Lng2); + Assert.assertTrue(data_lng2[0] == -128); + Assert.assertTrue(data_lng2[1] == 0); + Assert.assertTrue(data_lng2[2] == 0); + Assert.assertTrue(data_lng2[3] == 0); + Assert.assertTrue(data_lng2[4] == 0); + Assert.assertTrue(data_lng2[5] == 0); + Assert.assertTrue(data_lng2[6] == 0); + Assert.assertTrue(data_lng2[7] == 0); + + byte B = Byte.MAX_VALUE; + byte[] data_B = new byte[Byte.SIZE / 8]; + data_B = BitBufferHelper.toByteArray(B); + Assert.assertTrue(data_B[0] == 127); + + byte B1 = Byte.MIN_VALUE; + byte[] data_B1 = new byte[Byte.SIZE / 8]; + data_B1 = BitBufferHelper.toByteArray(B1); + Assert.assertTrue(data_B1[0] == -128); + + byte B2 = 64; + byte[] data_B2 = new byte[Byte.SIZE / 8]; + data_B2 = BitBufferHelper.toByteArray(B2); + Assert.assertTrue(data_B2[0] == 64); + + byte B3 = 32; + byte[] data_B3 = new byte[Byte.SIZE / 8]; + data_B3 = BitBufferHelper.toByteArray(B3); + Assert.assertTrue(data_B3[0] == 32); + + } + + @Test + public void testToByteArrayVariable() { + int len = 9; + byte[] data_sh; + data_sh = BitBufferHelper.toByteArray(511, len); + Assert.assertTrue(data_sh[0] == (byte) 255); + Assert.assertTrue(data_sh[1] == (byte) 128); + + data_sh = BitBufferHelper.toByteArray((int) 511, len); + Assert.assertTrue(data_sh[0] == (byte) 255); + Assert.assertTrue(data_sh[1] == (byte) 128); + + data_sh = BitBufferHelper.toByteArray((long) 511, len); + Assert.assertTrue(data_sh[0] == (byte) 255); + Assert.assertTrue(data_sh[1] == (byte) 128); + } + + @Test + public void testToInt() { + byte data[] = { 1 }; + Assert.assertTrue(BitBufferHelper.toNumber(data) == 1); + + byte data2[] = { 1, 1 }; + Assert.assertTrue(BitBufferHelper.toNumber(data2) == 257); + + byte data3[] = { 1, 1, 1 }; + Assert.assertTrue(BitBufferHelper.toNumber(data3) == 65793); + } + + @Test + public void testToLongGetter() { + byte data[] = { 1, 1 }; + Assert.assertTrue(BitBufferHelper.getLong(data) == 257L); + } + + @Test + public void testSetByte() throws Exception { + byte input; + byte[] data = new byte[20]; + + input = 125; + BitBufferHelper.setByte(data, input, 0, Byte.SIZE); + Assert.assertTrue(data[0] == 125); + + input = 109; + BitBufferHelper.setByte(data, input, 152, Byte.SIZE); + Assert.assertTrue(data[19] == 109); + } + + @Test + public void testSetBytes() throws Exception { + byte[] input = { 0, 1 }; + byte[] data = { 6, 0 }; + + BitBufferHelper.setBytes(data, input, 7, 9); + Assert.assertTrue(data[0] == 6); + Assert.assertTrue(data[1] == 1); + } + + //@Test + //INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [10100000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001]*/ + public void testInsertBits() throws Exception { + //CASE 1: startOffset%8 == 0 && numBits%8 == 0 + byte inputdata[] = { 75, 110, 107, 80, 10, 12, 35, 100, 125, 65 }; + int startOffset = 0; + int numBits = 8; + + byte data1[] = new byte[2]; + startOffset = 0; + numBits = 16; + BitBufferHelper.insertBits(data1, inputdata, startOffset, numBits); + Assert.assertTrue(data1[0] == 75); + Assert.assertTrue(data1[1] == 110); + + byte data2[] = new byte[4]; + startOffset = 0; + numBits = 32; + BitBufferHelper.insertBits(data2, inputdata, startOffset, numBits); + Assert.assertTrue(data2[0] == 75); + Assert.assertTrue(data2[1] == 110); + Assert.assertTrue(data2[2] == 107); + Assert.assertTrue(data2[3] == 80); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [10100000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] // OUTPUT: [01001011] [01101000] = {75, 104} + byte data10[] = new byte[2]; + startOffset = 0; + numBits = 13; + BitBufferHelper.insertBits(data10, inputdata, startOffset, numBits); + Assert.assertTrue(data10[0] == 75); + Assert.assertTrue(data10[1] == 104); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [10100000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] // OUTPUT: [01001000] = {72} + byte data11[] = new byte[4]; + startOffset = 8; + numBits = 6; + BitBufferHelper.insertBits(data11, inputdata, startOffset, numBits); + Assert.assertTrue(data11[1] == 72); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [10100000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [01001011] [01101110] [01101000] = {75, 110, 105} + byte data12[] = new byte[4]; + startOffset = 0; + numBits = 23; + BitBufferHelper.insertBits(data12, inputdata, startOffset, numBits); + Assert.assertTrue(data12[0] == 75); + Assert.assertTrue(data12[1] == 110); + Assert.assertTrue(data12[2] == 106); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [10100000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [01001011] [01101110] [01100000] = {75, 110, 96} + byte data13[] = new byte[4]; + startOffset = 8; + numBits = 20; + BitBufferHelper.insertBits(data13, inputdata, startOffset, numBits); + Assert.assertTrue(data13[1] == 75); + Assert.assertTrue(data13[2] == 110); + Assert.assertTrue(data13[3] == 96); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [10100000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [01001011] [01101110] [01101011] [10100000]= {75, 110, 107, 80} + byte data14[] = new byte[4]; + startOffset = 0; + numBits = 30; + BitBufferHelper.insertBits(data14, inputdata, startOffset, numBits); + Assert.assertTrue(data14[0] == 75); + Assert.assertTrue(data14[1] == 110); + Assert.assertTrue(data14[2] == 107); + Assert.assertTrue(data14[3] == 80); + + //CASE 3: startOffset%8 != 0, numBits%8 = 0 + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [10100000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00001001] [11000000] = {72, 96} + byte data16[] = new byte[5]; + startOffset = 3; + numBits = 8; + BitBufferHelper.insertBits(data16, inputdata, startOffset, numBits); + Assert.assertTrue(data16[0] == 9); + Assert.assertTrue(data16[1] == 96); + Assert.assertTrue(data16[2] == 0); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: [00000100] [1011 0110] [1110 0000] = {4, -54, -96} + + startOffset = 3; + numBits = 16; + byte data17[] = new byte[5]; + BitBufferHelper.insertBits(data17, inputdata, startOffset, numBits); + Assert.assertTrue(data17[0] == 9); + Assert.assertTrue(data17[1] == 109); + Assert.assertTrue(data17[2] == -64); + Assert.assertTrue(data17[3] == 0); + + // INPUT: {79, 110, 111} + // = [01001111] [01101110] [01101111] + //OUTPUT: [0000 1001] [1110 1101] [110 00000] = {9, -19, -64} + byte data18[] = new byte[5]; + byte inputdata3[] = { 79, 110, 111 }; + startOffset = 3; + numBits = 16; + BitBufferHelper.insertBits(data18, inputdata3, startOffset, numBits); + Assert.assertTrue(data18[0] == 9); + Assert.assertTrue(data18[1] == -19); + Assert.assertTrue(data18[2] == -64); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: [0000 1001] [0110 1101] [1100 1101] [0110 1010] [0000 0001] = {9, 109, -51, 106, 0} + + startOffset = 3; + numBits = 32; + byte data19[] = new byte[5]; + BitBufferHelper.insertBits(data19, inputdata, startOffset, numBits); + Assert.assertTrue(data19[0] == 9); + Assert.assertTrue(data19[1] == 109); + Assert.assertTrue(data19[2] == -51); + Assert.assertTrue(data19[3] == 106); + Assert.assertTrue(data19[4] == 0); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: data[4, 5, 6] = [0 010 0101] [1 011 0111] [0 000 0000] = {37, -73, 0} + startOffset = 33; + numBits = 16; + byte data20[] = new byte[7]; + BitBufferHelper.insertBits(data20, inputdata, startOffset, numBits); + Assert.assertTrue(data20[4] == 37); + Assert.assertTrue(data20[5] == -73); + Assert.assertTrue(data20[6] == 0); + + //CASE 4: extranumBits != 0 AND extraOffsetBits != 0 + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: [0000 1001] [0100 0000] = {9, 96} + startOffset = 3; + numBits = 7; + byte data21[] = new byte[7]; + BitBufferHelper.insertBits(data21, inputdata, startOffset, numBits); + Assert.assertTrue(data21[0] == 9); + Assert.assertTrue(data21[1] == 64); + Assert.assertTrue(data21[2] == 0); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: data = [00000 010] [01011 011] [01110 000] = {37, -73, 0} + startOffset = 5; + numBits = 17; + byte data22[] = new byte[7]; + BitBufferHelper.insertBits(data22, inputdata, startOffset, numBits); + Assert.assertTrue(data22[0] == 2); + Assert.assertTrue(data22[1] == 91); + Assert.assertTrue(data22[2] == 112); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: [0000 1001] [0110 1101] [110 01101] [01 00000] = {9, 109, -51, 64} + startOffset = 3; + numBits = 23; + byte data23[] = new byte[7]; + BitBufferHelper.insertBits(data23, inputdata, startOffset, numBits); + Assert.assertTrue(data23[0] == 9); + Assert.assertTrue(data23[1] == 109); + Assert.assertTrue(data23[2] == -51); + Assert.assertTrue(data23[3] == 64); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: [0000 1001] [0110 1101] = {9, 109} + startOffset = 3; + numBits = 13; + byte data24[] = new byte[7]; + BitBufferHelper.insertBits(data24, inputdata, startOffset, numBits); + Assert.assertTrue(data24[0] == 9); + Assert.assertTrue(data24[1] == 109); + Assert.assertTrue(data24[2] == 0); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: [0000 0100] [1011 0110] [1110 0110] = {4, -74, -26} + startOffset = 4; + numBits = 20; + byte data25[] = new byte[7]; + BitBufferHelper.insertBits(data25, inputdata, startOffset, numBits); + Assert.assertTrue(data25[0] == 4); + Assert.assertTrue(data25[1] == -74); + Assert.assertTrue(data25[2] == -26); + Assert.assertTrue(data25[3] == -0); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: [0000 0010] [0101 1011] = {0, 2, 91, 0} + startOffset = 13; + numBits = 11; + byte data26[] = new byte[7]; + BitBufferHelper.insertBits(data26, inputdata, startOffset, numBits); + Assert.assertTrue(data26[0] == 0); + Assert.assertTrue(data26[1] == 2); + Assert.assertTrue(data26[2] == 91); + Assert.assertTrue(data26[3] == 0); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: [000 01001] [011 01101] [110 0 0000] = {9, 109, -64, 0} + startOffset = 3; + numBits = 17; + byte data27[] = new byte[7]; + BitBufferHelper.insertBits(data27, inputdata, startOffset, numBits); + Assert.assertTrue(data27[0] == 9); + Assert.assertTrue(data27[1] == 109); + Assert.assertTrue(data27[2] == -64); + Assert.assertTrue(data27[3] == 0); + + // INPUT: {75, 110, 107, 80, 10, 12, 35, 100, 125, 65} = + // [01001011] [01101110] [01101011] [01010000] [00001010] [00001100] [00100011] [01100100] [11111101] [01000001] //OUTPUT: [00000000] [00000100] [10110110] [11100000]= {0, 4, -54, -96} + // OUTPUT: [00 000000] [00 000000] [00 010010] [11 011011] [10 011010] [11 010100] [0000 0000] = {0, 0, 18, -37,-102,-44,0} + startOffset = 18; + numBits = 34; + byte data28[] = new byte[7]; + BitBufferHelper.insertBits(data28, inputdata, startOffset, numBits); + Assert.assertTrue(data28[0] == 0); + Assert.assertTrue(data28[1] == 0); + Assert.assertTrue(data28[2] == 18); + Assert.assertTrue(data28[3] == -37); + Assert.assertTrue(data28[4] == -102); + Assert.assertTrue(data28[5] == -44); + Assert.assertTrue(data28[6] == 0); + + } + + @Test + public void testGetShort() throws Exception { + byte data[] = new byte[2]; + data[0] = 7; + data[1] = 8; + int length = 9; // num bits + Assert.assertTrue(BitBufferHelper.getShort(data, length) == 264); + + data[0] = 6; + data[1] = 8; + short result = BitBufferHelper.getShort(data, length); + Assert.assertTrue(result == 8); + + data[0] = 8; + data[1] = 47; + result = BitBufferHelper.getShort(data, length); + Assert.assertTrue(result == 47); + + //[0000 0001] [0001 0100] [0110 0100] + byte[] data1 = new byte[2]; + data1[0] = 1; + data1[1] = 20; //data1[2] = 100; + length = 15; + result = BitBufferHelper.getShort(data1, length); + Assert.assertTrue(result == 276); + + byte[] data2 = new byte[2]; + data2[0] = 64; + data2[1] = 99; //data2[2] = 100; + length = 13; + result = BitBufferHelper.getShort(data2, length); + Assert.assertTrue(result == 99); + + byte[] data3 = { 100, 50 }; + result = BitBufferHelper.getShort(data3); + Assert.assertTrue(result == 25650); + } + + @Test + public void testToIntVarLength() throws Exception { + byte data[] = { (byte) 255, (byte) 128 }; + int length = 9; // num bits + Assert.assertTrue(BitBufferHelper.getInt(data, length) == 384); + + byte data2[] = { 0, 8 }; + Assert.assertTrue(BitBufferHelper.getInt(data2, 9) == 8); + + byte data3[] = { 1, 1, 1 }; + Assert.assertTrue(BitBufferHelper.getInt(data3) == 65793); + + byte data4[] = { 1, 1, 1 }; + Assert.assertTrue(BitBufferHelper.getInt(data4) == 65793); + + byte data5[] = { 1, 1 }; + Assert.assertTrue(BitBufferHelper.getInt(data5) == 257); + + } + + @Test + public void testShiftBitstoLSB() { + byte[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + byte[] data2 = { 8, 9, 10 }; + byte[] shiftedBytes2 = BitBufferHelper.shiftBitsToLSB(data2, 11); + + Assert.assertTrue(shiftedBytes2[0] == 0); + Assert.assertTrue(shiftedBytes2[1] == 64); + Assert.assertTrue(shiftedBytes2[2] == 72); + + byte[] shiftedBytes = BitBufferHelper.shiftBitsToLSB(data, 49); + + Assert.assertTrue(shiftedBytes[0] == 0); + Assert.assertTrue(shiftedBytes[1] == 2); + Assert.assertTrue(shiftedBytes[2] == 4); + Assert.assertTrue(shiftedBytes[3] == 6); + Assert.assertTrue(shiftedBytes[4] == 8); + Assert.assertTrue(shiftedBytes[5] == 10); + Assert.assertTrue(shiftedBytes[6] == 12); + Assert.assertTrue(shiftedBytes[7] == 14); + Assert.assertTrue(shiftedBytes[8] == 16); + Assert.assertTrue(shiftedBytes[9] == 18); + + byte[] data1 = { 1, 2, 3 }; + byte[] shiftedBytes1 = BitBufferHelper.shiftBitsToLSB(data1, 18); + Assert.assertTrue(shiftedBytes1[0] == 0); + Assert.assertTrue(shiftedBytes1[1] == 4); + Assert.assertTrue(shiftedBytes1[2] == 8); + + } + + @Test + public void testShiftBitstoLSBMSB() { + byte[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; + + byte[] clone = BitBufferHelper.shiftBitsToMSB(BitBufferHelper + .shiftBitsToLSB(data, 72), 72); + + Assert.assertTrue(clone[0] == 1); + Assert.assertTrue(clone[1] == 2); + Assert.assertTrue(clone[2] == 3); + Assert.assertTrue(clone[3] == 4); + Assert.assertTrue(clone[4] == 5); + Assert.assertTrue(clone[5] == 6); + Assert.assertTrue(clone[6] == 7); + Assert.assertTrue(clone[7] == 8); + Assert.assertTrue(clone[8] == 9); + Assert.assertTrue(clone[9] == 0); + } + +} diff --git a/opendaylight/commons/liblldp/src/test/java/org/opendaylight/controller/sal/packet/address/EthernetAddressTest.java b/opendaylight/commons/liblldp/src/test/java/org/opendaylight/controller/sal/packet/address/EthernetAddressTest.java new file mode 100644 index 0000000000..cfdc7851e3 --- /dev/null +++ b/opendaylight/commons/liblldp/src/test/java/org/opendaylight/controller/sal/packet/address/EthernetAddressTest.java @@ -0,0 +1,114 @@ + +/* + * 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 + */ + +/** + * @file EthernetAddressTest.java + * + * @brief Unit Tests for EthernetAddress class + * + * Unit Tests for EthernetAddress class + */ +package org.opendaylight.controller.sal.packet.address; + +import org.junit.Assert; +import org.junit.Test; +import org.opendaylight.controller.liblldp.ConstructionException; +import org.opendaylight.controller.liblldp.EthernetAddress; + +public class EthernetAddressTest { + @Test + public void testNonValidConstructor() { + @SuppressWarnings("unused") + EthernetAddress ea1; + // Null input array + try { + ea1 = new EthernetAddress((byte[]) null); + + // Exception is expected if NOT raised test will fail + Assert.assertTrue(false); + } catch (ConstructionException e) { + } + + // Array too short + try { + ea1 = new EthernetAddress(new byte[] { (byte) 0x0, (byte) 0x0 }); + + // Exception is expected if NOT raised test will fail + Assert.assertTrue(false); + } catch (ConstructionException e) { + } + + // Array too long + try { + ea1 = new EthernetAddress(new byte[] { (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0 }); + + // Exception is expected if NOT raised test will fail + Assert.assertTrue(false); + } catch (ConstructionException e) { + } + } + + @Test + public void testEquality() { + EthernetAddress ea1; + EthernetAddress ea2; + try { + ea1 = new EthernetAddress(new byte[] { (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1 }); + + ea2 = new EthernetAddress(new byte[] { (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1 }); + Assert.assertTrue(ea1.equals(ea2)); + } catch (ConstructionException e) { + // Exception is NOT expected if raised test will fail + Assert.assertTrue(false); + } + + try { + ea1 = new EthernetAddress(new byte[] { (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1 }); + + ea2 = ea1.clone(); + Assert.assertTrue(ea1.equals(ea2)); + } catch (ConstructionException e) { + // Exception is NOT expected if raised test will fail + Assert.assertTrue(false); + } + + // Check for well knowns + try { + ea1 = EthernetAddress.BROADCASTMAC; + ea2 = new EthernetAddress(new byte[] { (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }); + Assert.assertTrue(ea1.equals(ea2)); + } catch (ConstructionException e) { + // Exception is NOT expected if raised test will fail + Assert.assertTrue(false); + } + } + + @Test + public void testUnEquality() { + EthernetAddress ea1; + EthernetAddress ea2; + try { + ea1 = new EthernetAddress(new byte[] { (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2 }); + + ea2 = new EthernetAddress(new byte[] { (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1 }); + Assert.assertTrue(!ea1.equals(ea2)); + } catch (ConstructionException e) { + // Exception is NOT expected if raised test will fail + Assert.assertTrue(false); + } + } +} diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 8797c7e85c..66a403560e 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -64,6 +64,13 @@ 0.5.2-SNAPSHOT 1.4 0.2.5-SNAPSHOT + etc/opendaylight/karaf + 00-netty.xml + 01-mdsal.xml + 01-netconf.xml + 03-toaster-sample.xml + 10-rest-connector.xml + 99-netconf-connector.xml 0.4.3-SNAPSHOT 0.4.3-SNAPSHOT 0.1.2-SNAPSHOT @@ -83,6 +90,7 @@ 0000.0002.0038.0 + 1.6.0 1.4.2-SNAPSHOT 2.4.0 0.4.2-SNAPSHOT @@ -850,6 +858,11 @@ config-persister-file-xml-adapter ${config.version} + + org.opendaylight.controller + config-persister-feature-adapter + ${config.version} + org.opendaylight.controller config-persister-impl @@ -999,6 +1012,11 @@ karaf.branding ${karaf.branding.version} + + org.opendaylight.controller + liblldp + ${sal.version} + org.opendaylight.controller logback-config @@ -1209,6 +1227,11 @@ sal-common-util ${mdsal.version} + + org.opendaylight.controller + sal-inmemory-datastore + ${mdsal.version} + org.opendaylight.controller sal-compatibility @@ -1547,6 +1570,38 @@ toaster-config ${mdsal.version} + + org.opendaylight.yangtools + features-yangtools + ${yangtools.version} + features + xml + runtime + + + org.opendaylight.controller.samples + features-toaster + ${mdsal.version} + features + xml + runtime + + + org.opendaylight.controller + features-config-netty + ${config.version} + features + xml + runtime + + + org.opendaylight.controller + features-flow + ${mdsal.version} + features + xml + runtime + org.opendaylight.controller.thirdparty com.sun.jersey.jersey-servlet @@ -1809,7 +1864,7 @@ org.opendaylight.controller - config-features + features-config ${config.version} features xml @@ -1817,7 +1872,7 @@ org.opendaylight.controller - features-odl-protocol-framework + features-protocol-framework ${protocol-framework.version} features xml @@ -1825,7 +1880,7 @@ org.opendaylight.controller - netconf-features + features-netconf ${netconf.version} features xml @@ -1833,7 +1888,7 @@ org.opendaylight.controller - config-persister-features + features-config-persister ${config.version} features xml @@ -1857,7 +1912,7 @@ org.opendaylight.controller - mdsal-features + features-mdsal ${mdsal.version} features xml diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigPusher.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigPusher.java new file mode 100644 index 0000000000..2dade8a82b --- /dev/null +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigPusher.java @@ -0,0 +1,21 @@ +/* + * 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.config.persist.api; + +import java.util.List; +/* + * The config pusher service pushes configs into the config subsystem + */ +public interface ConfigPusher { + + /* + * Pushes configs into the config subsystem + */ + + public void pushConfigs(List configs) throws InterruptedException; +} diff --git a/opendaylight/config/config-persister-feature-adapter/pom.xml b/opendaylight/config/config-persister-feature-adapter/pom.xml new file mode 100644 index 0000000000..7412a51425 --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/pom.xml @@ -0,0 +1,74 @@ + + + + 4.0.0 + + + org.opendaylight.controller + config-subsystem + 0.2.5-SNAPSHOT + .. + + + config-persister-feature-adapter + bundle + + + + org.osgi + org.osgi.core + provided + + + org.apache.karaf.features + org.apache.karaf.features.core + ${karaf.version} + provided + + + org.opendaylight.controller + config-persister-impl + + + org.opendaylight.controller + config-persister-api + + + org.opendaylight.controller + config-persister-directory-xml-adapter + + + org.apache.felix + org.apache.felix.utils + 1.6.0 + provided + + + com.google.guava + guava + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.version} + org.opendaylight.controller.configpusherfeature.ConfigPusherFeatureActivator + + org.apache.karaf.features.internal.model, + org.apache.felix.utils.version, + org.opendaylight.controller.configpusherfeature.internal + + + + + + + + diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/ConfigPusherFeatureActivator.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/ConfigPusherFeatureActivator.java new file mode 100644 index 0000000000..ea99579f16 --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/ConfigPusherFeatureActivator.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.configpusherfeature; + +import org.opendaylight.controller.config.persist.api.ConfigPusher; +import org.opendaylight.controller.configpusherfeature.internal.ConfigPusherCustomizer; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; + +public class ConfigPusherFeatureActivator implements BundleActivator { + + BundleContext bc = null; + ConfigPusherCustomizer cpc = null; + ServiceTracker cpst = null; + + public void start(BundleContext context) throws Exception { + bc = context; + cpc = new ConfigPusherCustomizer(); + cpst = new ServiceTracker(bc, ConfigPusher.class.getName(), cpc); + cpst.open(); + } + + public void stop(BundleContext context) throws Exception { + if(cpst != null) { + cpst.close(); + cpst = null; + } + if(cpc != null) { + cpc.close(); + cpc = null; + } + bc = null; + } +} diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/AbstractFeatureWrapper.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/AbstractFeatureWrapper.java new file mode 100644 index 0000000000..1bf2025c46 --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/AbstractFeatureWrapper.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.configpusherfeature.internal; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import javax.xml.bind.JAXBException; + +import org.apache.karaf.features.BundleInfo; +import org.apache.karaf.features.Conditional; +import org.apache.karaf.features.ConfigFileInfo; +import org.apache.karaf.features.Dependency; +import org.apache.karaf.features.Feature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +/* + * Wrap a Feature for the purposes of extracting the FeatureConfigSnapshotHolders from + * its underlying ConfigFileInfo's + * + * Delegates the the contained feature and provides additional methods. + */ +public class AbstractFeatureWrapper implements Feature { + private static final Logger logger = LoggerFactory.getLogger(AbstractFeatureWrapper.class); + protected Feature feature = null; + + protected AbstractFeatureWrapper() { + // prevent instantiation without Feature + } + + /* + * @param f Feature to wrap + */ + public AbstractFeatureWrapper(Feature f) { + Preconditions.checkNotNull(f,"FeatureWrapper requires non-null Feature in constructor"); + this.feature = f; + } + + /* + * Get FeatureConfigSnapshotHolders appropriate to feed to the config subsystem + * from the underlying Feature Config files + */ + public LinkedHashSet getFeatureConfigSnapshotHolders() throws Exception { + LinkedHashSet snapShotHolders = new LinkedHashSet(); + for(ConfigFileInfo c: getConfigurationFiles()) { + try { + snapShotHolders.add(new FeatureConfigSnapshotHolder(c,this)); + } catch (JAXBException e) { + logger.debug("{} is not a config subsystem config file",c.getFinalname()); + } + } + return snapShotHolders; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((feature == null) ? 0 : feature.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AbstractFeatureWrapper other = (AbstractFeatureWrapper) obj; + if (feature == null) { + if (other.feature != null) + return false; + } else if (!feature.equals(other.feature)) + return false; + return true; + } + + @Override + public String toString() { + return feature.getName(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getId() + */ + public String getId() { + return feature.getId(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getName() + */ + public String getName() { + return feature.getName(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getDescription() + */ + public String getDescription() { + return feature.getDescription(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getDetails() + */ + public String getDetails() { + return feature.getDetails(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getVersion() + */ + public String getVersion() { + return feature.getVersion(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#hasVersion() + */ + public boolean hasVersion() { + return feature.hasVersion(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getResolver() + */ + public String getResolver() { + return feature.getResolver(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getInstall() + */ + public String getInstall() { + return feature.getInstall(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getDependencies() + */ + public List getDependencies() { + return feature.getDependencies(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getBundles() + */ + public List getBundles() { + return feature.getBundles(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getConfigurations() + */ + public Map> getConfigurations() { + return feature.getConfigurations(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getConfigurationFiles() + */ + public List getConfigurationFiles() { + return feature.getConfigurationFiles(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getConditional() + */ + public List getConditional() { + return feature.getConditional(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getStartLevel() + */ + public int getStartLevel() { + return feature.getStartLevel(); + } + + /** + * @return + * @see org.apache.karaf.features.Feature#getRegion() + */ + public String getRegion() { + return feature.getRegion(); + } + +} \ No newline at end of file diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ChildAwareFeatureWrapper.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ChildAwareFeatureWrapper.java new file mode 100644 index 0000000000..8d2ae68a9a --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ChildAwareFeatureWrapper.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.configpusherfeature.internal; + +import java.util.LinkedHashSet; +import java.util.List; + +import javax.xml.bind.JAXBException; + +import org.apache.felix.utils.version.VersionRange; +import org.apache.felix.utils.version.VersionTable; +import org.apache.karaf.features.Dependency; +import org.apache.karaf.features.Feature; +import org.apache.karaf.features.FeaturesService; +import org.osgi.framework.Version; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +/* + * Wrap a Feature for the purposes of extracting the FeatureConfigSnapshotHolders from + * its underlying ConfigFileInfo's and those of its children recursively + * + * Delegates the the contained feature and provides additional methods. + */ +public class ChildAwareFeatureWrapper extends AbstractFeatureWrapper implements Feature { + private static final Logger logger = LoggerFactory.getLogger(ChildAwareFeatureWrapper.class); + private FeaturesService featuresService= null; + + protected ChildAwareFeatureWrapper(Feature f) { + // Don't use without a feature service + } + + /* + * @param f Feature to wrap + * @param s FeaturesService to look up dependencies + */ + ChildAwareFeatureWrapper(Feature f, FeaturesService s) throws Exception { + super(s.getFeature(f.getName(), f.getVersion())); + Preconditions.checkNotNull(s, "FeatureWrapper requires non-null FeatureService in constructor"); + this.featuresService = s; + } + + protected FeaturesService getFeaturesService() { + return featuresService; + } + + /* + * Get FeatureConfigSnapshotHolders appropriate to feed to the config subsystem + * from the underlying Feature Config files and those of its children recursively + */ + public LinkedHashSet getChildFeatures() throws Exception { + List dependencies = feature.getDependencies(); + LinkedHashSet childFeatures = new LinkedHashSet(); + if(dependencies != null) { + for(Dependency dependency: dependencies) { + Feature fi = extractFeatureFromDependency(dependency); + if(fi != null){ + ChildAwareFeatureWrapper wrappedFeature = new ChildAwareFeatureWrapper(fi,featuresService); + childFeatures.add(wrappedFeature); + } + } + } + return childFeatures; + } + + public LinkedHashSet getFeatureConfigSnapshotHolders() throws Exception { + LinkedHashSet snapShotHolders = new LinkedHashSet(); + for(ChildAwareFeatureWrapper c: getChildFeatures()) { + for(FeatureConfigSnapshotHolder h: c.getFeatureConfigSnapshotHolders()) { + FeatureConfigSnapshotHolder f; + try { + f = new FeatureConfigSnapshotHolder(h,this); + snapShotHolders.add(f); + } catch (JAXBException e) { + logger.debug("{} is not a config subsystem config file",h.getFileInfo().getFinalname()); + } + } + } + snapShotHolders.addAll(super.getFeatureConfigSnapshotHolders()); + return snapShotHolders; + } + + protected Feature extractFeatureFromDependency(Dependency dependency) throws Exception { + Feature[] features = featuresService.listFeatures(); + VersionRange range = org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION.equals(dependency.getVersion()) + ? VersionRange.ANY_VERSION : new VersionRange(dependency.getVersion(), true, true); + Feature fi = null; + for(Feature f: features) { + if (f.getName().equals(dependency.getName())) { + Version v = VersionTable.getVersion(f.getVersion()); + if (range.contains(v)) { + if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) { + fi = f; + break; + } + } + } + } + return fi; + } + +} diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ConfigFeaturesListener.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ConfigFeaturesListener.java new file mode 100644 index 0000000000..f5f1b856ac --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ConfigFeaturesListener.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.configpusherfeature.internal; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.apache.karaf.features.FeatureEvent; +import org.apache.karaf.features.FeaturesListener; +import org.apache.karaf.features.FeaturesService; +import org.apache.karaf.features.RepositoryEvent; +import org.opendaylight.controller.config.persist.api.ConfigPusher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConfigFeaturesListener implements FeaturesListener, AutoCloseable { + private static final Logger logger = LoggerFactory.getLogger(ConfigFeaturesListener.class); + private static final int QUEUE_SIZE = 100; + private BlockingQueue queue = new LinkedBlockingQueue(QUEUE_SIZE); + Thread pushingThread = null; + + public ConfigFeaturesListener(ConfigPusher p, FeaturesService f) { + pushingThread = new Thread(new ConfigPushingRunnable(p, f, queue), "ConfigFeatureListener - ConfigPusher"); + pushingThread.start(); + } + + @Override + public void featureEvent(FeatureEvent event) { + queue.offer(event); + } + + @Override + public void repositoryEvent(RepositoryEvent event) { + logger.debug("Repository: " + event.getType() + " " + event.getRepository()); + } + + @Override + public void close() { + if(pushingThread != null) { + pushingThread.interrupt(); + pushingThread = null; + } + } +} diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ConfigPusherCustomizer.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ConfigPusherCustomizer.java new file mode 100644 index 0000000000..d33a8cba92 --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ConfigPusherCustomizer.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.configpusherfeature.internal; + +import org.apache.karaf.features.FeaturesService; +import org.opendaylight.controller.config.persist.api.ConfigPusher; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConfigPusherCustomizer implements ServiceTrackerCustomizer, AutoCloseable { + private static final Logger logger = LoggerFactory.getLogger(ConfigPusherCustomizer.class); + private ConfigFeaturesListener configFeaturesListener = null; + private FeatureServiceCustomizer featureServiceCustomizer = null; + private ServiceTracker fsst = null; + + @Override + public ConfigPusher addingService(ServiceReference configPusherServiceReference) { + logger.trace("Got ConfigPusherCustomizer.addingService {}", configPusherServiceReference); + BundleContext bc = configPusherServiceReference.getBundle().getBundleContext(); + ConfigPusher cpService = bc.getService(configPusherServiceReference); + featureServiceCustomizer = new FeatureServiceCustomizer(cpService); + fsst = new ServiceTracker(bc, FeaturesService.class.getName(), featureServiceCustomizer); + fsst.open(); + return cpService; + } + + @Override + public void modifiedService(ServiceReference configPusherServiceReference, ConfigPusher configPusher) { + // we don't care if the properties change + } + + @Override + public void removedService(ServiceReference configPusherServiceReference, ConfigPusher configPusher) { + this.close(); + } + + @Override + public void close() { + if(fsst != null) { + fsst.close(); + fsst = null; + } + if(configFeaturesListener != null) { + configFeaturesListener.close(); + configFeaturesListener = null; + } + if(featureServiceCustomizer != null) { + featureServiceCustomizer.close(); + featureServiceCustomizer = null; + } + } +} \ No newline at end of file diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ConfigPushingRunnable.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ConfigPushingRunnable.java new file mode 100644 index 0000000000..06c5c920f4 --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/ConfigPushingRunnable.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.configpusherfeature.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.apache.karaf.features.Feature; +import org.apache.karaf.features.FeatureEvent; +import org.apache.karaf.features.FeatureEvent.EventType; +import org.apache.karaf.features.FeaturesService; +import org.opendaylight.controller.config.persist.api.ConfigPusher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.LinkedHashMultimap; + +public class ConfigPushingRunnable implements Runnable { + private static final Logger logger = LoggerFactory.getLogger(ConfigPushingRunnable.class); + private static final int POLL_TIME = 1; + private BlockingQueue queue; + private FeatureConfigPusher configPusher; + public ConfigPushingRunnable(ConfigPusher p, FeaturesService f,BlockingQueue q) { + queue = q; + configPusher = new FeatureConfigPusher(p, f); + } + + @Override + public void run() { + List toInstall = new ArrayList(); + FeatureEvent event; + boolean interuppted = false; + while(true) { + try { + if(!interuppted) { + if(toInstall.isEmpty()) { + event = queue.take(); + } else { + event = queue.poll(POLL_TIME, TimeUnit.MILLISECONDS); + } + if(event != null && event.getFeature() !=null) { + processFeatureEvent(event,toInstall); + } + } else if(toInstall.isEmpty()) { + logger.error("ConfigPushingRunnable - exiting"); + return; + } + } catch (InterruptedException e) { + logger.error("ConfigPushingRunnable - interupted"); + interuppted = true; + } catch (Exception e) { + logger.error("Exception while processing features {}", e); + } + } + } + + protected void processFeatureEvent(FeatureEvent event, List toInstall) throws InterruptedException, Exception { + if(event.getType() == EventType.FeatureInstalled) { + toInstall.add(event.getFeature()); + LinkedHashMultimap result = configPusher.pushConfigs(toInstall); + toInstall.removeAll(result.keySet()); + } else if(event.getType() == EventType.FeatureUninstalled) { + toInstall.remove(event.getFeature()); + } + } + + protected void logPushResult(LinkedHashMultimap results) { + for(Feature f:results.keySet()) { + logger.info("Pushed configs for feature {} {}",f,results.get(f)); + } + } +} diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigPusher.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigPusher.java new file mode 100644 index 0000000000..1c094ad2dc --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigPusher.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.configpusherfeature.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; + +import org.apache.karaf.features.Feature; +import org.apache.karaf.features.FeaturesService; +import org.opendaylight.controller.config.persist.api.ConfigPusher; +import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.LinkedHashMultimap; + +/* + * Simple class to push configs to the config subsystem from Feature's configfiles + */ +public class FeatureConfigPusher { + private static final Logger logger = LoggerFactory.getLogger(FeatureConfigPusher.class); + private FeaturesService featuresService = null; + private ConfigPusher pusher = null; + /* + * A LinkedHashSet (to preserve order and insure uniqueness) of the pushedConfigs + * This is used to prevent pushing duplicate configs if a Feature is in multiple dependency + * chains. Also, preserves the *original* Feature chain for which we pushed the config. + * (which is handy for logging). + */ + LinkedHashSet pushedConfigs = new LinkedHashSet(); + /* + * LinkedHashMultimap to track which configs we pushed for each Feature installation + * For future use + */ + LinkedHashMultimap feature2configs = LinkedHashMultimap.create(); + + /* + * @param p - ConfigPusher to push ConfigSnapshotHolders + */ + public FeatureConfigPusher(ConfigPusher p, FeaturesService f) { + pusher = p; + featuresService = f; + } + /* + * Push config files from Features to config subsystem + * @param features - list of Features to extract config files from recursively and push + * to the config subsystem + * + * @return A LinkedHashMultimap of Features to the FeatureConfigSnapshotHolder actually pushed + * If a Feature is not in the returned LinkedHashMultimap then we couldn't push its configs + * (Ususally because it was not yet installed) + */ + public LinkedHashMultimap pushConfigs(List features) throws Exception, InterruptedException { + LinkedHashMultimap pushedFeatures = LinkedHashMultimap.create(); + for(Feature feature: features) { + LinkedHashSet configSnapShots = pushConfig(feature); + if(!configSnapShots.isEmpty()) { + pushedFeatures.putAll(feature,configSnapShots); + } + } + return pushedFeatures; + } + + private LinkedHashSet pushConfig(Feature feature) throws Exception, InterruptedException { + LinkedHashSet configs = new LinkedHashSet(); + if(isInstalled(feature)) { + ChildAwareFeatureWrapper wrappedFeature = new ChildAwareFeatureWrapper(feature,featuresService); + configs = wrappedFeature.getFeatureConfigSnapshotHolders(); + if(!configs.isEmpty()) { + configs = pushConfig(configs); + feature2configs.putAll(feature, configs); + } + } + return configs; + } + + private boolean isInstalled(Feature feature) { + List installedFeatures = Arrays.asList(featuresService.listInstalledFeatures()); + return installedFeatures.contains(feature); + } + + private LinkedHashSet pushConfig(LinkedHashSet configs) throws InterruptedException { + LinkedHashSet configsToPush = new LinkedHashSet(configs); + configsToPush.removeAll(pushedConfigs); + if(!configsToPush.isEmpty()) { + pusher.pushConfigs(new ArrayList(configsToPush)); + pushedConfigs.addAll(configsToPush); + } + LinkedHashSet configsPushed = new LinkedHashSet(pushedConfigs); + configsPushed.retainAll(configs); + return configsPushed; + } +} diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigSnapshotHolder.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigSnapshotHolder.java new file mode 100644 index 0000000000..d1a92ebe7f --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigSnapshotHolder.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.configpusherfeature.internal; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.SortedSet; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import org.apache.karaf.features.ConfigFileInfo; +import org.apache.karaf.features.Feature; +import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; +import org.opendaylight.controller.config.persist.storage.file.xml.model.ConfigSnapshot; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +/* + * A ConfigSnapshotHolder that can track all the additional information + * relavent to the fact we are getting these from a Feature. + * + * Includes tracking the 'featureChain' - an reverse ordered list of the dependency + * graph of features that caused us to push this FeatureConfigSnapshotHolder. + * So if A -> B -> C, then the feature chain would be C -> B -> A + */ +public class FeatureConfigSnapshotHolder implements ConfigSnapshotHolder { + private ConfigSnapshot unmarshalled = null; + private ConfigFileInfo fileInfo = null; + private List featureChain = new ArrayList(); + + /* + * @param holder - FeatureConfigSnapshotHolder that we + * @param feature - new + */ + public FeatureConfigSnapshotHolder(final FeatureConfigSnapshotHolder holder, final Feature feature) throws JAXBException { + this(holder.fileInfo,holder.getFeature()); + this.featureChain.add(feature); + } + + /* + * Create a FeatureConfigSnapshotHolder for a given ConfigFileInfo and record the associated + * feature we are creating it from. + * @param fileInfo - ConfigFileInfo to read into the ConfigSnapshot + * @param feature - Feature the ConfigFileInfo was attached to + */ + public FeatureConfigSnapshotHolder(final ConfigFileInfo fileInfo, final Feature feature) throws JAXBException { + Preconditions.checkNotNull(fileInfo); + Preconditions.checkNotNull(fileInfo.getFinalname()); + Preconditions.checkNotNull(feature); + this.fileInfo = fileInfo; + this.featureChain.add(feature); + JAXBContext jaxbContext = JAXBContext.newInstance(ConfigSnapshot.class); + Unmarshaller um = jaxbContext.createUnmarshaller(); + File file = new File(fileInfo.getFinalname()); + unmarshalled = ((ConfigSnapshot) um.unmarshal(file)); + } + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + * + * We really care most about the underlying ConfigShapshot, so compute hashcode on that + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((unmarshalled != null && unmarshalled.getConfigSnapshot() == null) ? 0 : unmarshalled.getConfigSnapshot().hashCode()); + return result; + } + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + * * + * We really care most about the underlying ConfigShapshot, so compute equality on that + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + FeatureConfigSnapshotHolder fcsh = (FeatureConfigSnapshotHolder)obj; + if(this.unmarshalled.getConfigSnapshot().equals(fcsh.unmarshalled.getConfigSnapshot())) { + return true; + } + return false; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + Path p = Paths.get(fileInfo.getFinalname()); + b.append(p.getFileName()) + .append("(") + .append(getCauseFeature()) + .append(",") + .append(getFeature()) + .append(")"); + return b.toString(); + + } + + @Override + public String getConfigSnapshot() { + return unmarshalled.getConfigSnapshot(); + } + + @Override + public SortedSet getCapabilities() { + return unmarshalled.getCapabilities(); + } + + public ConfigFileInfo getFileInfo() { + return fileInfo; + } + + /* + * @returns The original feature to which the ConfigFileInfo was attached + * Example: + * A -> B -> C, ConfigFileInfo Foo is attached to C. + * feature:install A + * thus C is the 'Feature' Foo was attached. + */ + public Feature getFeature() { + return featureChain.get(0); + } + + /* + * @return The dependency chain of the features that caused the ConfigFileInfo to be pushed in reverse order. + * Example: + * A -> B -> C, ConfigFileInfo Foo is attached to C. + * The returned list is + * [C,B,A] + */ + public ImmutableList getFeatureChain() { + return ImmutableList.copyOf(Lists.reverse(featureChain)); + } + + /* + * @return The feature the installation of which was the root cause + * of this pushing of the ConfigFileInfo. + * Example: + * A -> B -> C, ConfigFileInfo Foo is attached to C. + * feature:install A + * this A is the 'Cause' of the installation of Foo. + */ + public Feature getCauseFeature() { + return Iterables.getLast(featureChain); + } +} diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureServiceCustomizer.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureServiceCustomizer.java new file mode 100644 index 0000000000..e72c8278e5 --- /dev/null +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureServiceCustomizer.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.configpusherfeature.internal; + +import org.apache.karaf.features.FeaturesListener; +import org.apache.karaf.features.FeaturesService; +import org.opendaylight.controller.config.persist.api.ConfigPusher; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.ServiceTrackerCustomizer; + +public class FeatureServiceCustomizer implements ServiceTrackerCustomizer, AutoCloseable { + private ConfigPusher configPusher = null; + private ConfigFeaturesListener configFeaturesListener = null; + private ServiceRegistration registration; + + FeatureServiceCustomizer(ConfigPusher c) { + configPusher = c; + } + + + @Override + public FeaturesService addingService(ServiceReference reference) { + BundleContext bc = reference.getBundle().getBundleContext(); + FeaturesService featureService = bc.getService(reference); + configFeaturesListener = new ConfigFeaturesListener(configPusher,featureService); + registration = bc.registerService(FeaturesListener.class.getCanonicalName(), configFeaturesListener, null); + return featureService; + } + + @Override + public void modifiedService(ServiceReference reference, + FeaturesService service) { + // we don't care if the properties change + + } + + @Override + public void removedService(ServiceReference reference, + FeaturesService service) { + close(); + } + + @Override + public void close() { + if(registration != null) { + registration.unregister(); + registration = null; + } + } + +} diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index 343d13e9c1..b8ad26116a 100644 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -23,6 +23,7 @@ config-util config-persister-api config-persister-file-xml-adapter + config-persister-feature-adapter yang-jmx-generator yang-jmx-generator-plugin yang-test diff --git a/opendaylight/distribution/opendaylight-karaf/pom.xml b/opendaylight/distribution/opendaylight-karaf/pom.xml index 5effbb09fc..b3c3f20ba8 100644 --- a/opendaylight/distribution/opendaylight-karaf/pom.xml +++ b/opendaylight/distribution/opendaylight-karaf/pom.xml @@ -61,14 +61,6 @@ kar runtime - - org.opendaylight.controller - config-netty-features - features - xml - runtime - - org.opendaylight.controller @@ -89,7 +81,14 @@ org.opendaylight.controller - mdsal-features + features-mdsal + features + xml + runtime + + + org.opendaylight.controller + features-flow features xml runtime diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/00-netty.xml b/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/00-netty.xml deleted file mode 100644 index 2365c700f9..0000000000 --- a/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/00-netty.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - urn:opendaylight:params:xml:ns:yang:controller:netty?module=netty&revision=2013-11-19 - urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12 - urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup?module=threadgroup&revision=2013-11-07 - urn:opendaylight:params:xml:ns:yang:controller:netty:timer?module=netty-timer&revision=2013-11-19 - - - - - - - netty:netty-threadgroup-fixed - global-boss-group - - - netty:netty-threadgroup-fixed - global-worker-group - - - netty:netty-hashed-wheel-timer - global-timer - - - netty:netty-global-event-executor - singleton - - - - - - netty:netty-threadgroup - - global-boss-group - /modules/module[type='netty-threadgroup-fixed'][name='global-boss-group'] - - - global-worker-group - /modules/module[type='netty-threadgroup-fixed'][name='global-worker-group'] - - - - netty:netty-event-executor - - global-event-executor - /modules/module[type='netty-global-event-executor'][name='singleton'] - - - - netty:netty-timer - - global-timer - /modules/module[type='netty-hashed-wheel-timer'][name='global-timer'] - - - - - - - diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/01-md-sal.xml b/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/01-md-sal.xml deleted file mode 100644 index 619ab06d8d..0000000000 --- a/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/01-md-sal.xml +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - prefix:schema-service-singleton - yang-schema-service - - - - - prefix:hash-map-data-store - hash-map-data-store - - - - - - - prefix:dom-broker-impl - dom-broker - - - dom:dom-data-store - - hash-map-data-store - - - - - - - - - prefix:binding-broker-impl - binding-broker-impl - - binding:binding-notification-service - binding-notification-broker - - - binding:binding-data-broker - binding-data-broker - - - - prefix:runtime-generated-mapping - runtime-mapping-singleton - - - prefix:binding-notification-broker - binding-notification-broker - - - - prefix:binding-data-broker - binding-data-broker - - dom:dom-broker-osgi-registry - dom-broker - - - binding:binding-dom-mapping-service - runtime-mapping-singleton - - - - - - - - - - dom:schema-service - - yang-schema-service - /modules/module[type='schema-service-singleton'][name='yang-schema-service'] - - - - binding:binding-notification-service - - binding-notification-broker - /modules/module[type='binding-notification-broker'][name='binding-notification-broker'] - - - - - dom:dom-data-store - - hash-map-data-store - /modules/module[type='hash-map-data-store'][name='hash-map-data-store'] - - - - - - - - binding:binding-broker-osgi-registry - - binding-osgi-broker - /modules/module[type='binding-broker-impl'][name='binding-broker-impl'] - - - - binding:binding-rpc-registry - - binding-rpc-broker - /modules/module[type='binding-broker-impl'][name='binding-broker-impl'] - - - - binding-impl:binding-dom-mapping-service - - runtime-mapping-singleton - /modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton'] - - - - dom:dom-broker-osgi-registry - - dom-broker - /modules/module[type='dom-broker-impl'][name='dom-broker'] - - - - - binding:binding-data-broker - - binding-data-broker - - /modules/module[type='binding-data-broker'][name='binding-data-broker'] - - - - - - - - - - - - urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12 - urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09 - urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 - urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28 - urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28 - urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28 - urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28 - - diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/03-toaster-sample.xml b/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/03-toaster-sample.xml deleted file mode 100644 index c481485c92..0000000000 --- a/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/03-toaster-sample.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - prefix:toaster-provider-impl - - toaster-provider-impl - - - binding:binding-rpc-registry - binding-rpc-broker - - - - - binding:binding-notification-service - - binding-notification-broker - - - - - - prefix:toaster-consumer-impl - - toaster-consumer-impl - - - binding:binding-rpc-registry - binding-rpc-broker - - - - - binding:binding-notification-service - - binding-notification-broker - - - - - - - toaster:toaster-provider - - toaster-provider - /modules/module[type='toaster-provider-impl'][name='toaster-provider-impl'] - - - - toaster:toaster-consumer - - toaster-consumer - /modules/module[type='toaster-consumer-impl'][name='toaster-consumer-impl'] - - - - - - - - - urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 - urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer?module=toaster-consumer&revision=2014-01-31 - urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl?module=toaster-consumer-impl&revision=2014-01-31 - urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider?module=toaster-provider&revision=2014-01-31 - urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&revision=2014-01-31 - - - - diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/custom.properties b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/custom.properties index 6c1ca421c2..e9a6992521 100644 --- a/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/custom.properties +++ b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/custom.properties @@ -25,18 +25,11 @@ netconf.ssh.port=1830 netconf.ssh.pk.path = ./configuration/RSA.pk -netconf.config.persister.active=1,2 -# read startup configuration -netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter -netconf.config.persister.1.properties.directoryStorage=configuration/initial/ - -# include only xml files, files with other extensions will be skipped, multiple extensions are permitted e.g. netconf.config.persister.1.properties.includeExtensions=xml,cfg,config -netconf.config.persister.1.properties.includeExtensions=xml -netconf.config.persister.1.readonly=true - -netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter -netconf.config.persister.2.properties.fileStorage=configuration/current/controller.currentconfig.xml -netconf.config.persister.2.properties.numberOfBackups=1 +netconf.config.persister.active=1 + +netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter +netconf.config.persister.1.properties.fileStorage=etc/opendaylight/current/controller.currentconfig.xml +netconf.config.persister.1.properties.numberOfBackups=1 # logback configuration logback.configurationFile=configuration/logback.xml diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties index e91da89970..a98956e98d 100644 --- a/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties +++ b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties @@ -180,7 +180,8 @@ jre-1.6= \ org.w3c.dom.xpath, \ org.xml.sax, \ org.xml.sax.ext, \ - org.xml.sax.helpers + org.xml.sax.helpers, \ + javax.annotation.processing # Standard package set. Note that: # - javax.transaction* is exported with a mandatory attribute @@ -341,7 +342,8 @@ jre-1.7= \ org.w3c.dom.xpath, \ org.xml.sax, \ org.xml.sax.ext, \ - org.xml.sax.helpers + org.xml.sax.helpers, \ + javax.annotation.processing jre-1.8= \ javax.accessibility, \ @@ -500,4 +502,5 @@ jre-1.8= \ org.w3c.dom.xpath, \ org.xml.sax, \ org.xml.sax.ext, \ - org.xml.sax.helpers + org.xml.sax.helpers, \ + javax.annotation.processing diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 7ab56e6d03..969ecc2cbe 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -1106,6 +1106,10 @@ org.opendaylight.controller.md topology-lldp-discovery + + org.opendaylight.controller + liblldp + org.opendaylight.controller.md topology-manager diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RoutingTable.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RoutingTable.java index c25aa523e2..d21d05d7fe 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RoutingTable.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RoutingTable.java @@ -14,7 +14,6 @@ import org.opendaylight.controller.remote.rpc.registry.gossip.Copier; import org.opendaylight.controller.sal.connector.api.RpcRouter; import java.io.Serializable; -import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -26,7 +25,7 @@ public class RoutingTable implements Copier, Serializable { @Override public RoutingTable copy() { RoutingTable copy = new RoutingTable(); - copy.setTable(Collections.unmodifiableMap(table)); + copy.setTable(new HashMap<>(table)); copy.setRouter(this.getRouter()); return copy; @@ -53,6 +52,9 @@ public class RoutingTable implements Copier, Serializable { return table.containsKey(routeId); } + public Boolean isEmpty(){ + return table.isEmpty(); + } /// /// Getter, Setters /// diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java index 51609870cc..e2ebcb2b25 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java @@ -28,9 +28,10 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoute; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.RemoveRoute; +import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoutes; +import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.RemoveRoutes; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; +import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRouters; import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBuckets; import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBucketsReply; import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetLocalBucket; @@ -39,10 +40,9 @@ import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.Bu /** * Registry to look up cluster nodes that have registered for a given rpc. - *

+ *

* It uses {@link org.opendaylight.controller.remote.rpc.registry.gossip.BucketStore} to maintain this * cluster wide information. - * */ public class RpcRegistry extends UntypedActor { @@ -76,14 +76,14 @@ public class RpcRegistry extends UntypedActor { if (message instanceof SetLocalRouter) receiveSetLocalRouter((SetLocalRouter) message); - if (message instanceof AddOrUpdateRoute) - receiveAddRoute((AddOrUpdateRoute) message); + if (message instanceof AddOrUpdateRoutes) + receiveAddRoutes((AddOrUpdateRoutes) message); - else if (message instanceof RemoveRoute) - receiveRemoveRoute((RemoveRoute) message); + else if (message instanceof RemoveRoutes) + receiveRemoveRoutes((RemoveRoutes) message); else if (message instanceof Messages.FindRouters) - receiveGetRouter((Messages.FindRouters) message); + receiveGetRouter((FindRouters) message); else unhandled(message); @@ -95,55 +95,40 @@ public class RpcRegistry extends UntypedActor { * @param message contains {@link akka.actor.ActorRef} for rpc broker */ private void receiveSetLocalRouter(SetLocalRouter message) { - if (message == null || message.getRouter() == null) - return;//ignore - localRouter = message.getRouter(); } /** - * //TODO: update this to accept multiple route registration * @param msg */ - private void receiveAddRoute(AddOrUpdateRoute msg) { - if (msg.getRouteIdentifier() == null) - return;//ignore + private void receiveAddRoutes(AddOrUpdateRoutes msg) { Preconditions.checkState(localRouter != null, "Router must be set first"); Future futureReply = Patterns.ask(bucketStore, new GetLocalBucket(), 1000); - futureReply.map(getMapperToAddRoute(msg.getRouteIdentifier()), getContext().dispatcher()); + futureReply.map(getMapperToAddRoutes(msg.getRouteIdentifiers()), getContext().dispatcher()); } /** - * //TODO: update this to accept multiple routes - * @param msg + * @param msg contains list of route ids to remove */ - private void receiveRemoveRoute(RemoveRoute msg) { - if (msg.getRouteIdentifier() == null) - return;//ignore + private void receiveRemoveRoutes(RemoveRoutes msg) { Future futureReply = Patterns.ask(bucketStore, new GetLocalBucket(), 1000); - futureReply.map(getMapperToRemoveRoute(msg.getRouteIdentifier()), getContext().dispatcher()); + futureReply.map(getMapperToRemoveRoutes(msg.getRouteIdentifiers()), getContext().dispatcher()); } /** * Finds routers for the given rpc. + * * @param msg */ - private void receiveGetRouter(Messages.FindRouters msg) { + private void receiveGetRouter(FindRouters msg) { final ActorRef sender = getSender(); - //if empty message, return empty list - if (msg.getRouteIdentifier() == null) { - sender.tell(createEmptyReply(), getSelf()); - return; - } - Future futureReply = Patterns.ask(bucketStore, new GetAllBuckets(), 1000); futureReply.map(getMapperToGetRouter(msg.getRouteIdentifier(), sender), getContext().dispatcher()); - } /** @@ -158,6 +143,7 @@ public class RpcRegistry extends UntypedActor { /** * Helper to create a reply when routers are found for the given rpc + * * @param buckets * @param routeId * @return @@ -165,18 +151,15 @@ public class RpcRegistry extends UntypedActor { private Messages.FindRoutersReply createReplyWithRouters(Map buckets, RpcRouter.RouteIdentifier routeId) { List> routers = new ArrayList<>(); - Option> routerWithUpdateTime = null; for (Bucket bucket : buckets.values()) { RoutingTable table = (RoutingTable) bucket.getData(); - if (table == null) continue; routerWithUpdateTime = table.getRouterFor(routeId); - if (routerWithUpdateTime.isEmpty()) continue; @@ -192,7 +175,7 @@ public class RpcRegistry extends UntypedActor { /// /** - * Receives all buckets returned from bucket store and finds routers for the buckets where given rpc(routeId) is found + * Receives all buckets returned from bucket store and finds routers for the buckets where given rpc(routeId) is found * * @param routeId the rpc * @param sender client who asked to find the routers. @@ -224,10 +207,10 @@ public class RpcRegistry extends UntypedActor { * Receives local bucket from bucket store and updates routing table in it by removing the route. Subsequently, * it updates the local bucket in bucket store. * - * @param routeId rpc to remote + * @param routeIds rpc to remote * @return */ - private Mapper getMapperToRemoveRoute(final RpcRouter.RouteIdentifier routeId) { + private Mapper getMapperToRemoveRoutes(final List> routeIds) { return new Mapper() { @Override public Void apply(Object replyMessage) { @@ -246,8 +229,12 @@ public class RpcRegistry extends UntypedActor { table = new RoutingTable(); table.setRouter(localRouter); - table.removeRoute(routeId); + if (!table.isEmpty()) { + for (RpcRouter.RouteIdentifier routeId : routeIds) { + table.removeRoute(routeId); + } + } bucket.setData(table); UpdateBucket updateBucketMessage = new UpdateBucket(bucket); @@ -262,10 +249,10 @@ public class RpcRegistry extends UntypedActor { * Receives local bucket from bucket store and updates routing table in it by adding the route. Subsequently, * it updates the local bucket in bucket store. * - * @param routeId rpc to add + * @param routeIds rpc to add * @return */ - private Mapper getMapperToAddRoute(final RpcRouter.RouteIdentifier routeId) { + private Mapper getMapperToAddRoutes(final List> routeIds) { return new Mapper() { @Override @@ -285,7 +272,9 @@ public class RpcRegistry extends UntypedActor { table = new RoutingTable(); table.setRouter(localRouter); - table.addRoute(routeId); + for (RpcRouter.RouteIdentifier routeId : routeIds) { + table.addRoute(routeId); + } bucket.setData(table); @@ -305,47 +294,50 @@ public class RpcRegistry extends UntypedActor { public static class ContainsRoute { - final RpcRouter.RouteIdentifier routeIdentifier; + final List> routeIdentifiers; - public ContainsRoute(RpcRouter.RouteIdentifier routeIdentifier) { - Preconditions.checkArgument(routeIdentifier != null); - this.routeIdentifier = routeIdentifier; + public ContainsRoute(List> routeIdentifiers) { + Preconditions.checkArgument(routeIdentifiers != null && + !routeIdentifiers.isEmpty(), + "Route Identifiers must be supplied"); + this.routeIdentifiers = routeIdentifiers; } - public RpcRouter.RouteIdentifier getRouteIdentifier(){ - return this.routeIdentifier; + public List> getRouteIdentifiers() { + return this.routeIdentifiers; } @Override public String toString() { - return this.getClass().getSimpleName() + "{" + - "routeIdentifier=" + routeIdentifier + + return "ContainsRoute{" + + "routeIdentifiers=" + routeIdentifiers + '}'; } } - public static class AddOrUpdateRoute extends ContainsRoute{ + public static class AddOrUpdateRoutes extends ContainsRoute { - public AddOrUpdateRoute(RpcRouter.RouteIdentifier routeIdentifier) { - super(routeIdentifier); + public AddOrUpdateRoutes(List> routeIdentifiers) { + super(routeIdentifiers); } } - public static class RemoveRoute extends ContainsRoute { + public static class RemoveRoutes extends ContainsRoute { - public RemoveRoute(RpcRouter.RouteIdentifier routeIdentifier) { - super(routeIdentifier); + public RemoveRoutes(List> routeIdentifiers) { + super(routeIdentifiers); } } - public static class SetLocalRouter{ + public static class SetLocalRouter { private final ActorRef router; public SetLocalRouter(ActorRef router) { + Preconditions.checkArgument(router != null, "Router must not be null"); this.router = router; } - public ActorRef getRouter(){ + public ActorRef getRouter() { return this.router; } @@ -357,9 +349,23 @@ public class RpcRegistry extends UntypedActor { } } - public static class FindRouters extends ContainsRoute { + public static class FindRouters { + private final RpcRouter.RouteIdentifier routeIdentifier; + public FindRouters(RpcRouter.RouteIdentifier routeIdentifier) { - super(routeIdentifier); + Preconditions.checkArgument(routeIdentifier != null, "Route must not be null"); + this.routeIdentifier = routeIdentifier; + } + + public RpcRouter.RouteIdentifier getRouteIdentifier() { + return routeIdentifier; + } + + @Override + public String toString() { + return "FindRouters{" + + "routeIdentifier=" + routeIdentifier + + '}'; } } @@ -367,10 +373,11 @@ public class RpcRegistry extends UntypedActor { final List> routerWithUpdateTime; public FindRoutersReply(List> routerWithUpdateTime) { + Preconditions.checkArgument(routerWithUpdateTime != null, "List of routers found must not be null"); this.routerWithUpdateTime = routerWithUpdateTime; } - public List> getRouterWithUpdateTime(){ + public List> getRouterWithUpdateTime() { return routerWithUpdateTime; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java index 0b64136c49..2320789d59 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java @@ -41,15 +41,18 @@ import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.Go /** * Gossiper that syncs bucket store across nodes in the cluster. - *

- * It keeps a local scheduler that periodically sends Gossip ticks to itself to send bucket store's bucket versions - * to a randomly selected remote gossiper. - *

- * When bucket versions are received from a remote gossiper, it is compared with bucket store's bucket versions. - * Which ever buckets are newer locally, are sent to remote gossiper. If any bucket is older in bucket store, a - * gossip status is sent to remote gossiper so that it can send the newer buckets. - *

- * When a bucket is received from a remote gossiper, its sent to the bucket store for update. + *

+ * It keeps a local scheduler that periodically sends Gossip ticks to + * itself to send bucket store's bucket versions to a randomly selected remote + * gossiper. + *

+ * When bucket versions are received from a remote gossiper, it is compared + * with bucket store's bucket versions. Which ever buckets are newer + * locally, are sent to remote gossiper. If any bucket is older in bucket store, + * a gossip status is sent to remote gossiper so that it can send the newer buckets. + *

+ * When a bucket is received from a remote gossiper, its sent to the bucket store + * for update. * */ @@ -77,7 +80,8 @@ public class Gossiper extends UntypedActor { /** * Helpful for testing - * @param autoStartGossipTicks used for turning off gossip ticks during testing. Gossip tick can be manually sent. + * @param autoStartGossipTicks used for turning off gossip ticks during testing. + * Gossip tick can be manually sent. */ public Gossiper(Boolean autoStartGossipTicks){ this.autoStartGossipTicks = autoStartGossipTicks; @@ -94,7 +98,7 @@ public class Gossiper extends UntypedActor { if (autoStartGossipTicks) { gossipTask = getContext().system().scheduler().schedule( new FiniteDuration(1, TimeUnit.SECONDS), //initial delay - new FiniteDuration(500, TimeUnit.MILLISECONDS), //interval + new FiniteDuration(500, TimeUnit.MILLISECONDS), //interval getSelf(), //target new Messages.GossiperMessages.GossipTick(), //message getContext().dispatcher(), //execution context @@ -211,14 +215,12 @@ public class Gossiper extends UntypedActor { * @param status bucket versions from a remote member */ void receiveGossipStatus(GossipStatus status){ - //Dont want to accept messages from non-members + //Don't accept messages from non-members if (!clusterMembers.contains(status.from())) return; final ActorRef sender = getSender(); - Future futureReply = Patterns.ask(getContext().parent(), new GetBucketVersions(), 1000); - futureReply.map(getMapperToProcessRemoteStatus(sender, status), getContext().dispatcher()); } @@ -231,11 +233,9 @@ public class Gossiper extends UntypedActor { void receiveGossip(GossipEnvelope envelope){ //TODO: Add more validations if (!selfAddress.equals(envelope.to())) { - log.info("Ignoring message intended for someone else. From [{}] to [{}]", envelope.from(), envelope.to()); + log.debug("Ignoring message intended for someone else. From [{}] to [{}]", envelope.from(), envelope.to()); return; } - if (envelope.getBuckets() == null) - return; //nothing to do updateRemoteBuckets(envelope.getBuckets()); @@ -248,11 +248,7 @@ public class Gossiper extends UntypedActor { */ void updateRemoteBuckets(Map buckets) { - if (buckets == null || buckets.isEmpty()) - return; //nothing to merge - UpdateRemoteBuckets updateRemoteBuckets = new UpdateRemoteBuckets(buckets); - getContext().parent().tell(updateRemoteBuckets, getSelf()); } @@ -265,9 +261,7 @@ public class Gossiper extends UntypedActor { void sendGossipTo(final ActorRef remote, final Set
addresses){ Future futureReply = Patterns.ask(getContext().parent(), new GetBucketsByMembers(addresses), 1000); - futureReply.map(getMapperToSendGossip(remote), getContext().dispatcher()); - } /** @@ -279,7 +273,6 @@ public class Gossiper extends UntypedActor { //Get local status from bucket store and send to remote Future futureReply = Patterns.ask(getContext().parent(), new GetBucketVersions(), 1000); - ActorSelection remoteRef = getContext().system().actorSelection( remoteActorSystemAddress.toString() + getSelf().path().toStringWithoutAddress()); @@ -328,14 +321,16 @@ public class Gossiper extends UntypedActor { } /** - * Process bucket versions received from {@link org.opendaylight.controller.remote.rpc.registry.gossip.BucketStore}. + * Process bucket versions received from + * {@link org.opendaylight.controller.remote.rpc.registry.gossip.BucketStore}. * Then this method compares remote bucket versions with local bucket versions. *
    *
  • The buckets that are newer locally, send - * {@link org.opendaylight.controller.remote.rpc.registry.gossip.Messages.GossiperMessages.GossipEnvelope} to remote + * {@link org.opendaylight.controller.remote.rpc.registry.gossip.Messages.GossiperMessages.GossipEnvelope} + * to remote *
  • The buckets that are older locally, send - * {@link org.opendaylight.controller.remote.rpc.registry.gossip.Messages.GossiperMessages.GossipStatus} to remote so that - * remote sends GossipEnvelop. + * {@link org.opendaylight.controller.remote.rpc.registry.gossip.Messages.GossiperMessages.GossipStatus} + * to remote so that remote sends GossipEnvelop. *
* * @param sender the remote member @@ -390,9 +385,10 @@ public class Gossiper extends UntypedActor { } /** - * Processes the message from {@link org.opendaylight.controller.remote.rpc.registry.gossip.BucketStore} that contains - * {@link org.opendaylight.controller.remote.rpc.registry.gossip.Bucket}. These buckets are sent to a remote member encapsulated - * in {@link org.opendaylight.controller.remote.rpc.registry.gossip.Messages.GossiperMessages.GossipEnvelope} + * Processes the message from {@link org.opendaylight.controller.remote.rpc.registry.gossip.BucketStore} + * that contains {@link org.opendaylight.controller.remote.rpc.registry.gossip.Bucket}. + * These buckets are sent to a remote member encapsulated in + * {@link org.opendaylight.controller.remote.rpc.registry.gossip.Messages.GossiperMessages.GossipEnvelope} * * @param sender the remote member that sent * {@link org.opendaylight.controller.remote.rpc.registry.gossip.Messages.GossiperMessages.GossipStatus} @@ -407,7 +403,7 @@ public class Gossiper extends UntypedActor { public Void apply(Object msg) { if (msg instanceof GetBucketsByMembersReply) { Map buckets = ((GetBucketsByMembersReply) msg).getBuckets(); - log.info("Buckets to send from {}: {}", selfAddress, buckets); + log.debug("Buckets to send from {}: {}", selfAddress, buckets); GossipEnvelope envelope = new GossipEnvelope(selfAddress, sender.path().address(), buckets); sender.tell(envelope, getSelf()); } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Messages.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Messages.java index 9a247d97c7..bf8b20213b 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Messages.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Messages.java @@ -17,6 +17,10 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.ContainsBucketVersions; +import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.ContainsBuckets; + + /** * These messages are used by {@link org.opendaylight.controller.remote.rpc.registry.gossip.BucketStore} and * {@link org.opendaylight.controller.remote.rpc.registry.gossip.Gossiper} actors. @@ -107,7 +111,8 @@ public class Messages { Map versions; public ContainsBucketVersions(Map versions) { - Preconditions.checkArgument(versions != null, "versions can not be null"); + Preconditions.checkArgument(versions != null, "versions can not be null or empty"); + this.versions = versions; } @@ -135,7 +140,7 @@ public class Messages { public static final class GossipTick extends Tick {} - public static final class GossipStatus extends BucketStoreMessages.ContainsBucketVersions implements Serializable{ + public static final class GossipStatus extends ContainsBucketVersions implements Serializable{ private Address from; public GossipStatus(Address from, Map versions) { @@ -148,12 +153,13 @@ public class Messages { } } - public static final class GossipEnvelope extends BucketStoreMessages.ContainsBuckets implements Serializable { + public static final class GossipEnvelope extends ContainsBuckets implements Serializable { private final Address from; private final Address to; public GossipEnvelope(Address from, Address to, Map buckets) { super(buckets); + Preconditions.checkArgument(to != null, "Recipient of message must not be null"); this.to = to; this.from = from; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java index ab609413dd..da3942a828 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java @@ -24,10 +24,12 @@ import scala.concurrent.duration.FiniteDuration; import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoute; +import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoutes; +import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.RemoveRoutes; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRouters; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRoutersReply; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; @@ -83,13 +85,14 @@ public class RpcRegistryTest { /** * One node cluster. - * Register rpc. Ensure router can be found + * 1. Register rpc, ensure router can be found + * 2. Then remove rpc, ensure its deleted * * @throws URISyntaxException * @throws InterruptedException */ @Test - public void testWhenRpcAddedOneNodeShouldAppearOnSameNode() throws URISyntaxException, InterruptedException { + public void testAddRemoveRpcOnSameNode() throws URISyntaxException, InterruptedException { final JavaTestKit mockBroker = new JavaTestKit(node1); @@ -105,17 +108,28 @@ public class RpcRegistryTest { List> pairs = message.getRouterWithUpdateTime(); validateRouterReceived(pairs, mockBroker.getRef()); + + //Now remove rpc + registry1.tell(getRemoveRouteMessage(), mockBroker.getRef()); + Thread.sleep(1000); + //find the route on node 1's registry + registry1.tell(new FindRouters(createRouteId()), mockBroker.getRef()); + message = mockBroker.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); + pairs = message.getRouterWithUpdateTime(); + + Assert.assertTrue(pairs.isEmpty()); } /** * Three node cluster. - * Register rpc on 1 node. Ensure its router can be found on other 2. + * 1. Register rpc on 1 node, ensure its router can be found on other 2. + * 2. Remove rpc on 1 node, ensure its removed on other 2. * * @throws URISyntaxException * @throws InterruptedException */ @Test - public void testWhenRpcAddedOneNodeShouldAppearOnAnother() throws URISyntaxException, InterruptedException { + public void testRpcAddRemoveInCluster() throws URISyntaxException, InterruptedException { validateSystemStartup(); @@ -127,22 +141,25 @@ public class RpcRegistryTest { registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); - Thread.sleep(5000);// give some time for bucket store data sync + Thread.sleep(1000);// give some time for bucket store data sync //find the route in node 2's registry - registry2.tell(new FindRouters(createRouteId()), mockBroker2.getRef()); - FindRoutersReply message = mockBroker2.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - List> pairs = message.getRouterWithUpdateTime(); - + List> pairs = findRouters(registry2, mockBroker2); validateRouterReceived(pairs, mockBroker1.getRef()); //find the route in node 3's registry - registry3.tell(new FindRouters(createRouteId()), mockBroker3.getRef()); - message = mockBroker3.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - pairs = message.getRouterWithUpdateTime(); - + pairs = findRouters(registry3, mockBroker3); validateRouterReceived(pairs, mockBroker1.getRef()); + //Now remove + registry1.tell(getRemoveRouteMessage(), mockBroker1.getRef()); + Thread.sleep(1000);// give some time for bucket store data sync + + pairs = findRouters(registry2, mockBroker2); + Assert.assertTrue(pairs.isEmpty()); + + pairs = findRouters(registry3, mockBroker3); + Assert.assertTrue(pairs.isEmpty()); } /** @@ -171,7 +188,7 @@ public class RpcRegistryTest { registry2.tell(getAddRouteMessage(), mockBroker2.getRef()); registry3.tell(new SetLocalRouter(mockBroker3.getRef()), mockBroker3.getRef()); - Thread.sleep(5000);// give some time for bucket store data sync + Thread.sleep(1000);// give some time for bucket store data sync //find the route in node 3's registry registry3.tell(new FindRouters(createRouteId()), mockBroker3.getRef()); @@ -182,6 +199,12 @@ public class RpcRegistryTest { } + private List> findRouters(ActorRef registry, JavaTestKit receivingActor) throws URISyntaxException { + registry.tell(new FindRouters(createRouteId()), receivingActor.getRef()); + FindRoutersReply message = receivingActor.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); + return message.getRouterWithUpdateTime(); + } + private void validateMultiRouterReceived(List> actual, ActorRef... expected) { Assert.assertTrue(actual != null); Assert.assertTrue(actual.size() == expected.length); @@ -237,8 +260,19 @@ public class RpcRegistryTest { return resolved; } - private AddOrUpdateRoute getAddRouteMessage() throws URISyntaxException { - return new AddOrUpdateRoute(createRouteId()); + private AddOrUpdateRoutes getAddRouteMessage() throws URISyntaxException { + return new AddOrUpdateRoutes(createRouteIds()); + } + + private RemoveRoutes getRemoveRouteMessage() throws URISyntaxException { + return new RemoveRoutes(createRouteIds()); + } + + private List> createRouteIds() throws URISyntaxException { + QName type = new QName(new URI("/mockrpc"), "mockrpc"); + List> routeIds = new ArrayList<>(); + routeIds.add(new RouteIdentifierImpl(null, type, null)); + return routeIds; } private RpcRouter.RouteIdentifier createRouteId() throws URISyntaxException { diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/GossiperTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/GossiperTest.java index d862dcb8cd..f076c136fe 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/GossiperTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/GossiperTest.java @@ -115,17 +115,6 @@ public class GossiperTest { verify(mockGossiper, times(0)).updateRemoteBuckets(anyMap()); } - @Test - public void testUpdateRemoteBuckets_WhenNoBucketShouldIgnore(){ - - mockGossiper.updateRemoteBuckets(null); - verify(mockGossiper, times(0)).getContext(); - - Map empty = Collections.emptyMap(); - mockGossiper.updateRemoteBuckets(empty); - verify(mockGossiper, times(0)).getContext(); - } - /** * Create Gossiper actor and return the underlying instance of Gossiper class. * diff --git a/opendaylight/md-sal/sal-rest-connector-config/pom.xml b/opendaylight/md-sal/sal-rest-connector-config/pom.xml index 6d050cf425..fa91f0398d 100644 --- a/opendaylight/md-sal/sal-rest-connector-config/pom.xml +++ b/opendaylight/md-sal/sal-rest-connector-config/pom.xml @@ -17,4 +17,30 @@ sal-rest-connector-config Configuration files for sal-rest-connector jar + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/initial/10-rest-connector.xml + xml + config + + + + + + + + diff --git a/opendaylight/md-sal/sal-rest-connector-config/src/main/resources/initial/10-rest-connector.xml b/opendaylight/md-sal/sal-rest-connector-config/src/main/resources/initial/10-rest-connector.xml index 2fdc8c7d1e..3be423c356 100644 --- a/opendaylight/md-sal/sal-rest-connector-config/src/main/resources/initial/10-rest-connector.xml +++ b/opendaylight/md-sal/sal-rest-connector-config/src/main/resources/initial/10-rest-connector.xml @@ -14,7 +14,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html rest:rest-connector-impl rest-connector-default-impl - 8181 + 8185 dom:dom-broker-osgi-registry dom-broker @@ -35,4 +35,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html + + urn:opendaylight:params:xml:ns:yang:controller:md:sal:rest:connector?module=opendaylight-rest-connector&revision=2014-07-24 + diff --git a/opendaylight/md-sal/topology-lldp-discovery/pom.xml b/opendaylight/md-sal/topology-lldp-discovery/pom.xml index e01a0d5dcb..97ed15df19 100644 --- a/opendaylight/md-sal/topology-lldp-discovery/pom.xml +++ b/opendaylight/md-sal/topology-lldp-discovery/pom.xml @@ -35,11 +35,11 @@ org.opendaylight.controller - sal + sal-binding-api org.opendaylight.controller - sal-binding-api + liblldp org.opendaylight.controller.model diff --git a/opendaylight/md-sal/topology-lldp-discovery/src/main/java/org/opendaylight/md/controller/topology/lldp/utils/LLDPDiscoveryUtils.java b/opendaylight/md-sal/topology-lldp-discovery/src/main/java/org/opendaylight/md/controller/topology/lldp/utils/LLDPDiscoveryUtils.java index 82ab443246..0d1ba11ee1 100644 --- a/opendaylight/md-sal/topology-lldp-discovery/src/main/java/org/opendaylight/md/controller/topology/lldp/utils/LLDPDiscoveryUtils.java +++ b/opendaylight/md-sal/topology-lldp-discovery/src/main/java/org/opendaylight/md/controller/topology/lldp/utils/LLDPDiscoveryUtils.java @@ -9,10 +9,10 @@ package org.opendaylight.md.controller.topology.lldp.utils; import java.nio.charset.Charset; -import org.opendaylight.controller.sal.packet.Ethernet; -import org.opendaylight.controller.sal.packet.LLDP; -import org.opendaylight.controller.sal.packet.LLDPTLV; -import org.opendaylight.controller.sal.utils.NetUtils; +import org.opendaylight.controller.liblldp.Ethernet; +import org.opendaylight.controller.liblldp.LLDP; +import org.opendaylight.controller.liblldp.LLDPTLV; +import org.opendaylight.controller.liblldp.NetUtils; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java similarity index 85% rename from opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java rename to opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java index fff8d611b7..5f311b5232 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java @@ -10,9 +10,6 @@ package org.opendaylight.controller.netconf.persist.impl; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.base.Function; -import com.google.common.base.Stopwatch; -import com.google.common.collect.Collections2; import java.io.IOException; import java.io.InputStream; import java.util.Collection; @@ -23,10 +20,17 @@ import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; + import javax.annotation.concurrent.Immutable; +import javax.management.MBeanServerConnection; + import org.opendaylight.controller.config.api.ConflictingVersionException; +import org.opendaylight.controller.config.persist.api.ConfigPusher; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; +import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; @@ -45,22 +49,60 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; +import com.google.common.base.Function; +import com.google.common.base.Stopwatch; +import com.google.common.collect.Collections2; + @Immutable -public class ConfigPusher { - private static final Logger logger = LoggerFactory.getLogger(ConfigPusher.class); +public class ConfigPusherImpl implements ConfigPusher { + private static final Logger logger = LoggerFactory.getLogger(ConfigPusherImpl.class); private final long maxWaitForCapabilitiesMillis; private final long conflictingVersionTimeoutMillis; private final NetconfOperationServiceFactory configNetconfConnector; + private static final int QUEUE_SIZE = 100; + private BlockingQueue> queue = new LinkedBlockingQueue>(QUEUE_SIZE); - public ConfigPusher(NetconfOperationServiceFactory configNetconfConnector, long maxWaitForCapabilitiesMillis, + public ConfigPusherImpl(NetconfOperationServiceFactory configNetconfConnector, long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) { this.configNetconfConnector = configNetconfConnector; this.maxWaitForCapabilitiesMillis = maxWaitForCapabilitiesMillis; this.conflictingVersionTimeoutMillis = conflictingVersionTimeoutMillis; } - public synchronized LinkedHashMap pushConfigs(List configs) throws NetconfDocumentedException { + public void process(List autoCloseables, MBeanServerConnection platformMBeanServer, Persister persisterAggregator) throws InterruptedException { + List configs; + while(true) { + configs = queue.take(); + try { + internalPushConfigs(configs); + ConfigPersisterNotificationHandler jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator); + synchronized (autoCloseables) { + autoCloseables.add(jmxNotificationHandler); + } + /* + * We have completed initial configuration. At this point + * it is good idea to perform garbage collection to prune + * any garbage we have accumulated during startup. + */ + logger.debug("Running post-initialization garbage collection..."); + System.gc(); + logger.debug("Post-initialization garbage collection completed."); + logger.debug("ConfigPusher has pushed configs {}, gc completed", configs); + } + catch (NetconfDocumentedException e) { + logger.error("Error pushing configs {}",configs); + throw new IllegalStateException(e); + } + } + } + + public void pushConfigs(List configs) throws InterruptedException { + logger.debug("Requested to push configs {}", configs); + this.queue.put(configs); + } + + private LinkedHashMap internalPushConfigs(List configs) throws NetconfDocumentedException { logger.debug("Last config snapshots to be pushed to netconf: {}", configs); LinkedHashMap result = new LinkedHashMap<>(); // start pushing snapshots: @@ -278,7 +320,7 @@ public class ConfigPusher { private static NetconfMessage getCommitMessage() { String resource = "/netconfOp/commit.xml"; - try (InputStream stream = ConfigPusher.class.getResourceAsStream(resource)) { + try (InputStream stream = ConfigPusherImpl.class.getResourceAsStream(resource)) { checkNotNull(stream, "Unable to load resource " + resource); return new NetconfMessage(XmlUtil.readXmlToDocument(stream)); } catch (SAXException | IOException e) { 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 48ae0cb91a..0a48e6c67d 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,13 +8,18 @@ package org.opendaylight.controller.netconf.persist.impl.osgi; -import com.google.common.annotations.VisibleForTesting; +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.management.MBeanServer; + +import org.opendaylight.controller.config.persist.api.ConfigPusher; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; -import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; -import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler; -import org.opendaylight.controller.netconf.persist.impl.ConfigPusher; +import org.opendaylight.controller.netconf.persist.impl.ConfigPusherImpl; import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator; import org.opendaylight.controller.netconf.util.CloseableUtil; import org.osgi.framework.BundleActivator; @@ -23,16 +28,13 @@ import org.osgi.framework.Constants; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.management.MBeanServer; -import java.lang.management.ManagementFactory; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; +import com.google.common.annotations.VisibleForTesting; public class ConfigPersisterActivator implements BundleActivator { @@ -49,11 +51,15 @@ public class ConfigPersisterActivator implements BundleActivator { public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX = "storageAdapterClass"; private List autoCloseables; + private volatile BundleContext context; + ServiceRegistration registration; @Override public void start(final BundleContext context) throws Exception { logger.debug("ConfigPersister starting"); + this.context = context; + autoCloseables = new ArrayList<>(); PropertiesProviderBaseImpl propertiesProvider = new PropertiesProviderBaseImpl(context); @@ -81,8 +87,14 @@ public class ConfigPersisterActivator implements BundleActivator { } @Override - public synchronized void stop(BundleContext context) throws Exception { - CloseableUtil.closeAll(autoCloseables); + public void stop(BundleContext context) throws Exception { + synchronized(autoCloseables) { + CloseableUtil.closeAll(autoCloseables); + if (registration != null) { + registration.unregister(); + } + this.context = null; + } } @@ -147,35 +159,29 @@ public class ConfigPersisterActivator implements BundleActivator { logger.trace("Got InnerCustomizer.addingService {}", reference); NetconfOperationServiceFactory service = reference.getBundle().getBundleContext().getService(reference); - final ConfigPusher configPusher = new ConfigPusher(service, maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis); + logger.debug("Creating new job queue"); + + final ConfigPusherImpl configPusher = new ConfigPusherImpl(service, maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis); logger.debug("Configuration Persister got {}", service); + logger.debug("Context was {}", context); + logger.debug("Registration was {}", registration); + final Thread pushingThread = new Thread(new Runnable() { @Override public void run() { try { - configPusher.pushConfigs(configs); - } catch (NetconfDocumentedException e) { - logger.error("Error pushing configs {}",configs); - throw new IllegalStateException(e); + if(configs != null && !configs.isEmpty()) { + configPusher.pushConfigs(configs); + } + registration = context.registerService(ConfigPusher.class.getName(), configPusher, null); + configPusher.process(autoCloseables, platformMBeanServer, persisterAggregator); + } catch (InterruptedException e) { + logger.info("ConfigPusher thread stopped",e); } logger.info("Configuration Persister initialization completed."); - - /* - * We have completed initial configuration. At this point - * it is good idea to perform garbage collection to prune - * any garbage we have accumulated during startup. - */ - logger.debug("Running post-initialization garbage collection..."); - System.gc(); - logger.debug("Post-initialization garbage collection completed."); - - ConfigPersisterNotificationHandler jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator); - synchronized (ConfigPersisterActivator.this) { - autoCloseables.add(jmxNotificationHandler); - } } }, "config-pusher"); - synchronized (ConfigPersisterActivator.this) { + synchronized (autoCloseables) { autoCloseables.add(new AutoCloseable() { @Override public void close() { diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java index 95fd5f6549..3e5249468d 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java @@ -7,10 +7,23 @@ */ package org.opendaylight.controller.netconf.persist.impl.osgi; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collections; +import java.util.Dictionary; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.config.persist.api.ConfigPusher; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.config.persist.api.PropertiesProvider; @@ -23,18 +36,10 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; final class MockedBundleContext { @Mock @@ -49,6 +54,8 @@ final class MockedBundleContext { NetconfOperationServiceFactory serviceFactory; @Mock private NetconfOperationService service; + @Mock + private ServiceRegistration registration; MockedBundleContext(long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) throws Exception { MockitoAnnotations.initMocks(this); @@ -77,6 +84,11 @@ final class MockedBundleContext { doReturn(Collections.emptySet()).when(service).getCapabilities(); doNothing().when(service).close(); doReturn("serviceFactoryMock").when(serviceFactory).toString(); + + doNothing().when(registration).unregister(); + doReturn(registration).when(context).registerService( + eq(ConfigPusher.class.getName()), any(Closeable.class), + any(Dictionary.class)); } public BundleContext getBundleContext() { diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronPort.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronPort.java index b32b01cb3f..803e5e8d41 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronPort.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronPort.java @@ -235,6 +235,11 @@ public class NeutronPort extends ConfigurationObject implements Serializable, IN if (s.equals("tenant_id")) { ans.setTenantID(this.getTenantID()); } + if (s.equals("security_groups")) { + List securityGroups = new ArrayList(); + securityGroups.addAll(this.getSecurityGroups()); + ans.setSecurityGroups(securityGroups); + } } return ans; } diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronRoutersNorthbound.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronRoutersNorthbound.java index 806e853b36..0c02adad8a 100644 --- a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronRoutersNorthbound.java +++ b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronRoutersNorthbound.java @@ -527,6 +527,9 @@ public class NeutronRoutersNorthbound { if (input.getPortUUID() != null && input.getSubnetUUID() == null) { NeutronRouter_Interface targetInterface = target.getInterfaces().get(input.getPortUUID()); + if (targetInterface == null) { + throw new ResourceNotFoundException("Router interface not found for given Port UUID"); + } input.setSubnetUUID(targetInterface.getSubnetUUID()); input.setID(target.getID()); input.setTenantID(target.getTenantID()); @@ -554,7 +557,7 @@ public class NeutronRoutersNorthbound { throw new ResourceNotFoundException("Port UUID not found"); } if (port.getFixedIPs() == null) { - throw new ResourceNotFoundException("Port UUID jas no fixed IPs"); + throw new ResourceNotFoundException("Port UUID has no fixed IPs"); } NeutronSubnet subnet = subnetInterface.getSubnet(input.getSubnetUUID()); if (subnet == null) { diff --git a/pom.xml b/pom.xml index 8bebd2aa61..e4c51b7839 100644 --- a/pom.xml +++ b/pom.xml @@ -124,6 +124,7 @@ opendaylight/commons/parent opendaylight/commons/logback_settings opendaylight/commons/filter-valve + opendaylight/commons/liblldp opendaylight/dummy-console