From: Alessandro Boch Date: Mon, 9 Dec 2013 22:40:02 +0000 (+0000) Subject: Merge "Changed maximumEntries to correct int rather than long" X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~221 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=03abf047ba966c53f4901d36ae5198156d66dc05;hp=d046ec09656aa39bc26b6e3d6b01ca52cfa22af2 Merge "Changed maximumEntries to correct int rather than long" --- diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigSnapshotHolder.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigSnapshotHolder.java index 654326a898..37d29d746e 100644 --- a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigSnapshotHolder.java +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigSnapshotHolder.java @@ -4,14 +4,15 @@ import java.util.SortedSet; public interface ConfigSnapshotHolder { - /** - * Get part of get-config document that contains just - */ - String getConfigSnapshot(); + /** + * Get part of get-config document that contains just + */ + String getConfigSnapshot(); - /** - * Get only required capabilities referenced by the snapshot. - */ - SortedSet getCapabilities(); - } + /** + * Get only required capabilities referenced by the snapshot. + */ + SortedSet getCapabilities(); + +} diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigSnapshotHolderImpl.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigSnapshotHolderImpl.java new file mode 100644 index 0000000000..a0586df840 --- /dev/null +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigSnapshotHolderImpl.java @@ -0,0 +1,39 @@ +package org.opendaylight.controller.config.persist.api; + +import java.util.SortedSet; + +public class ConfigSnapshotHolderImpl implements ConfigSnapshotHolder { + + private final String snapshot; + private final SortedSet caps; + private final String fileName; + + public ConfigSnapshotHolderImpl(String configSnapshot, SortedSet capabilities, String fileName) { + this.snapshot = configSnapshot; + this.caps = capabilities; + this.fileName = fileName; + } + + @Override + public String getConfigSnapshot() { + return snapshot; + } + + @Override + public SortedSet getCapabilities() { + return caps; + } + + public String getFileName() { + return fileName; + } + + @Override + public String toString() { + return "ConfigSnapshotHolderImpl{" + + "snapshot='" + snapshot + '\'' + + ", caps=" + caps + + ", fileName='" + fileName + '\'' + + '}'; + } +} diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java index 1448e553e3..5509c99437 100644 --- a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java @@ -8,9 +8,8 @@ package org.opendaylight.controller.config.persist.api; -import com.google.common.base.Optional; - import java.io.IOException; +import java.util.List; /** * Base interface for persister implementation. @@ -19,7 +18,7 @@ public interface Persister extends AutoCloseable { void persistConfig(ConfigSnapshotHolder configSnapshotHolder) throws IOException; - Optional loadLastConfig() throws IOException; + List loadLastConfigs() throws IOException; @Override void close(); diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryPersister.java b/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryPersister.java index 25628b6041..39595edb0b 100644 --- a/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryPersister.java +++ b/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryPersister.java @@ -8,10 +8,10 @@ package org.opendaylight.controller.config.persist.storage.directory; import com.google.common.base.Charsets; -import com.google.common.base.Optional; import com.google.common.io.Files; import org.apache.commons.io.IOUtils; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; +import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolderImpl; import org.opendaylight.controller.config.persist.api.Persister; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,30 +64,25 @@ public class DirectoryPersister implements Persister { } @Override - public Optional loadLastConfig() throws IOException { + public List loadLastConfigs() throws IOException { File[] filesArray = storage.listFiles(); - if (filesArray.length == 0) { - return Optional.absent(); + if (filesArray == null || filesArray.length == 0) { + return Collections.emptyList(); } List sortedFiles = new ArrayList<>(Arrays.asList(filesArray)); Collections.sort(sortedFiles); // combine all found files + logger.debug("Reading files in following order: {}", sortedFiles); - SortedSet combinedCapabilities = new TreeSet<>(); - StringBuilder modulesBuilder = new StringBuilder(), servicesBuilder = new StringBuilder(); + List result = new ArrayList<>(); for (File file : sortedFiles) { logger.trace("Adding file '{}' to combined result", file); final MyLineProcessor lineProcessor = new MyLineProcessor(file.getAbsolutePath()); Files.readLines(file, ENCODING, lineProcessor); - - modulesBuilder.append(lineProcessor.getModules()); - servicesBuilder.append(lineProcessor.getServices()); - combinedCapabilities.addAll(lineProcessor.getCapabilities()); + result.add(lineProcessor.getConfigSnapshotHolder(header, middle, footer)); } - String combinedSnapshot = header + modulesBuilder.toString() + middle + servicesBuilder.toString() + footer; - ConfigSnapshotHolder result = new ConfigSnapshotHolderImpl(combinedSnapshot, combinedCapabilities); - return Optional.of(result); + return result; } @@ -165,25 +160,11 @@ class MyLineProcessor implements com.google.common.io.LineProcessor { return caps; } -} - -class ConfigSnapshotHolderImpl implements ConfigSnapshotHolder { - - private final String snapshot; - private final SortedSet caps; - - public ConfigSnapshotHolderImpl(String configSnapshot, SortedSet capabilities) { - this.snapshot = configSnapshot; - this.caps = capabilities; - } - - @Override - public String getConfigSnapshot() { - return snapshot; + ConfigSnapshotHolder getConfigSnapshotHolder(String header, String middle, String footer) { + String combinedSnapshot = header + getModules() + middle + getServices() + footer; + ConfigSnapshotHolder result = new ConfigSnapshotHolderImpl(combinedSnapshot, getCapabilities(), fileNameForReporting); + return result; } - @Override - public SortedSet getCapabilities() { - return caps; - } } + diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapterTest.java b/opendaylight/config/config-persister-directory-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapterTest.java index 53ab4c210e..f17e414c49 100644 --- a/opendaylight/config/config-persister-directory-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapterTest.java +++ b/opendaylight/config/config-persister-directory-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapterTest.java @@ -8,14 +8,13 @@ package org.opendaylight.controller.config.persist.storage.directory; -import com.google.common.base.Optional; import org.apache.commons.io.IOUtils; -import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import java.io.File; -import java.io.IOException; +import java.util.Collections; +import java.util.List; import java.util.SortedSet; import java.util.TreeSet; @@ -25,21 +24,13 @@ import static org.junit.Assert.fail; public class DirectoryStorageAdapterTest { DirectoryPersister tested; - SortedSet expectedCapabilities; - String expectedSnapshot; - - @Before - public void setUp() throws Exception { - expectedCapabilities = new TreeSet<>(IOUtils.readLines(getClass().getResourceAsStream("/expectedCapabilities.txt"))); - expectedSnapshot = IOUtils.toString(getClass().getResourceAsStream("/expectedSnapshot.xml")); - } @Test public void testEmptyDirectory() throws Exception { File folder = new File("target/emptyFolder"); folder.mkdir(); tested = new DirectoryPersister((folder)); - assertEquals(Optional.absent(), tested.loadLastConfig()); + assertEquals(Collections.emptyList(), tested.loadLastConfigs()); try { tested.persistConfig(new ConfigSnapshotHolder() { @@ -70,22 +61,28 @@ public class DirectoryStorageAdapterTest { public void testOneFile() throws Exception { File folder = getFolder("oneFile"); tested = new DirectoryPersister((folder)); - assertExpected(); + List results = tested.loadLastConfigs(); + assertEquals(1, results.size()); + ConfigSnapshotHolder result = results.get(0); + assertSnapshot(result, "oneFileExpected"); } - private void assertExpected() throws IOException { - Optional maybeResult = tested.loadLastConfig(); - assertTrue(maybeResult.isPresent()); - ConfigSnapshotHolder result = maybeResult.get(); - assertEquals(expectedCapabilities, result.getCapabilities()); - assertEquals(expectedSnapshot, result.getConfigSnapshot()); - } @Test public void testTwoFiles() throws Exception { File folder = getFolder("twoFiles"); tested = new DirectoryPersister((folder)); - assertExpected(); + List results = tested.loadLastConfigs(); + assertEquals(2, results.size()); + assertSnapshot(results.get(0), "twoFilesExpected1"); + assertSnapshot(results.get(1), "twoFilesExpected2"); + } + + private void assertSnapshot(ConfigSnapshotHolder result, String directory) throws Exception { + SortedSet expectedCapabilities = new TreeSet<>(IOUtils.readLines(getClass().getResourceAsStream("/" + directory + "/expectedCapabilities.txt"))); + String expectedSnapshot = IOUtils.toString(getClass().getResourceAsStream("/" + directory + "/expectedSnapshot.xml")); + assertEquals(expectedCapabilities, result.getCapabilities()); + assertEquals(expectedSnapshot, result.getConfigSnapshot()); } } diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/expectedCapabilities.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedCapabilities.txt similarity index 100% rename from opendaylight/config/config-persister-directory-adapter/src/test/resources/expectedCapabilities.txt rename to opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedCapabilities.txt diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/expectedSnapshot.xml b/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedSnapshot.xml similarity index 100% rename from opendaylight/config/config-persister-directory-adapter/src/test/resources/expectedSnapshot.xml rename to opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedSnapshot.xml diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedCapabilities.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedCapabilities.txt new file mode 100644 index 0000000000..ef35fddbce --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedCapabilities.txt @@ -0,0 +1 @@ +urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27 diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedSnapshot.xml b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedSnapshot.xml new file mode 100644 index 0000000000..2b1d06ecc0 --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedSnapshot.xml @@ -0,0 +1,84 @@ + + + + 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 + ref_hash-map-data-store + + + + prefix:binding-broker-impl + binding-broker-impl + + binding:binding-notification-service + ref_binding-notification-broker + + + binding:binding-data-broker + ref_binding-data-broker + + + + prefix:runtime-generated-mapping + runtime-mapping-singleton + + + prefix:binding-notification-broker + binding-notification-broker + + + + + dom:schema-service + + ref_yang-schema-service + /config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service'] + + + + binding:binding-notification-service + + ref_binding-notification-broker + /config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker'] + + + + dom:dom-data-store + + ref_hash-map-data-store + /config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store'] + + + + binding:binding-broker-osgi-registry + + ref_binding-broker-impl + /config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl'] + + + + binding-impl:binding-dom-mapping-service + + ref_runtime-mapping-singleton + /config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton'] + + + + dom:dom-broker-osgi-registry + + ref_dom-broker + /config/modules/module[name='dom-broker-impl']/instance[name='dom-broker'] + + + + diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedCapabilities.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedCapabilities.txt new file mode 100644 index 0000000000..9924111627 --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedCapabilities.txt @@ -0,0 +1,19 @@ +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:threadpool?module=threadpool&revision=2013-04-09 +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:config?module=config&revision=2013-04-05 +urn:ietf:params:netconf:capability:candidate:1.0 +urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04 +urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12 +urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17 +urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28 +urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24 +urn:ietf:params:netconf:capability:rollback-on-error:1.0 +urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24 +urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05 +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:logback:config?module=config-logging&revision=2013-07-16 +urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09 +urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16 +urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28 +urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19 diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedSnapshot.xml b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedSnapshot.xml new file mode 100644 index 0000000000..887cb2c1d4 --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedSnapshot.xml @@ -0,0 +1,25 @@ + + + + prefix:binding-data-broker + binding-data-broker + + dom:dom-broker-osgi-registry + ref_dom-broker + + + binding:binding-dom-mapping-service + ref_runtime-mapping-singleton + + + + + + binding:binding-data-broker + + ref_binding-data-broker + /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker'] + + + + diff --git a/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java b/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java index 66d0414d9a..3ec8713b47 100644 --- a/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java +++ b/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java @@ -15,17 +15,19 @@ import com.google.common.base.Preconditions; import com.google.common.io.Files; import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; +import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolderImpl; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.config.persist.api.PropertiesProvider; import org.opendaylight.controller.config.persist.api.StorageAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.xml.sax.SAXException; -import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -105,7 +107,7 @@ public class FileStorageAdapter implements StorageAdapter, Persister { } else { numberOfStoredBackups = Integer.MAX_VALUE; } - + logger.trace("Property {} set to {}", NUMBER_OF_BACKUPS, numberOfStoredBackups); return result; } @@ -164,27 +166,23 @@ public class FileStorageAdapter implements StorageAdapter, Persister { } @Override - public Optional loadLastConfig() throws IOException { + public List loadLastConfigs() throws IOException { Preconditions.checkNotNull(storage, "Storage file is null"); if (!storage.exists()) { - return Optional.absent(); + return Collections.emptyList(); } final LineProcessor lineProcessor = new LineProcessor(); - String result = Files.readLines(storage, ENCODING, lineProcessor); - - try { - if (lineProcessor.getConfigSnapshot().isPresent() == false) { - return Optional.absent(); - } else { - return Optional. of(new PersistedConfigImpl(lineProcessor.getConfigSnapshot(), - lineProcessor.getCapabilities())); - } + Files.readLines(storage, ENCODING, lineProcessor); - } catch (ParserConfigurationException | SAXException e) { - throw new IOException("Unable to load last config ", e); + if (lineProcessor.getConfigSnapshot().isPresent() == false) { + return Collections.emptyList(); + } else { + return Arrays.asList(new ConfigSnapshotHolderImpl(lineProcessor.getConfigSnapshot().get(), + lineProcessor.getCapabilities(), storage.getAbsolutePath())); } + } private static final class LineProcessor implements com.google.common.io.LineProcessor { @@ -227,15 +225,16 @@ public class FileStorageAdapter implements StorageAdapter, Persister { return true; } - Optional getConfigSnapshot() throws IOException, SAXException, ParserConfigurationException { + Optional getConfigSnapshot() { final String xmlContent = snapshotBuffer.toString(); - if (xmlContent == null || xmlContent.equals("")) { + if (xmlContent.equals("")) { return Optional.absent(); - } else + } else { return Optional.of(xmlContent); + } } - SortedSet getCapabilities() throws IOException, SAXException, ParserConfigurationException { + SortedSet getCapabilities() { return caps; } @@ -251,25 +250,4 @@ public class FileStorageAdapter implements StorageAdapter, Persister { return "FileStorageAdapter [storage=" + storage + "]"; } - private class PersistedConfigImpl implements ConfigSnapshotHolder { - - private final String snapshot; - private final SortedSet caps; - - public PersistedConfigImpl(Optional configSnapshot, SortedSet capabilities) { - this.snapshot = configSnapshot.get(); - this.caps = capabilities; - } - - @Override - public String getConfigSnapshot() { - return snapshot; - } - - @Override - public SortedSet getCapabilities() { - return caps; - } - } - } diff --git a/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java b/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java index ed50184aa7..0236598f2b 100644 --- a/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java +++ b/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java @@ -9,7 +9,6 @@ package org.opendaylight.controller.config.persist.storage.file; import com.google.common.base.Charsets; -import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import org.junit.Before; @@ -20,11 +19,11 @@ import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import java.io.File; import java.nio.file.Files; import java.util.Collection; +import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -75,11 +74,12 @@ public class FileStorageAdapterTest { }); assertEquals(14, readLines.size()); - Optional lastConf = storage.loadLastConfig(); - assertTrue(lastConf.isPresent()); + List lastConf = storage.loadLastConfigs(); + assertEquals(1, lastConf.size()); + ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0); assertEquals("2", - lastConf.get().getConfigSnapshot().replaceAll("\\s", "")); - assertEquals(createCaps(), lastConf.get().getCapabilities()); + configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", "")); + assertEquals(createCaps(), configSnapshotHolder.getCapabilities()); } private SortedSet createCaps() { @@ -123,10 +123,11 @@ public class FileStorageAdapterTest { }); assertEquals(7, readLines.size()); - Optional lastConf = storage.loadLastConfig(); - assertTrue(lastConf.isPresent()); + List lastConf = storage.loadLastConfigs(); + assertEquals(1, lastConf.size()); + ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0); assertEquals("2", - lastConf.get().getConfigSnapshot().replaceAll("\\s", "")); + configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", "")); } @Test @@ -163,10 +164,11 @@ public class FileStorageAdapterTest { assertEquals(14, readLines.size()); - Optional lastConf = storage.loadLastConfig(); - assertTrue(lastConf.isPresent()); + List lastConf = storage.loadLastConfigs(); + assertEquals(1, lastConf.size()); + ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0); assertEquals("3", - lastConf.get().getConfigSnapshot().replaceAll("\\s", "")); + configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", "")); assertFalse(readLines.contains(holder.getConfigSnapshot())); } @@ -178,14 +180,14 @@ public class FileStorageAdapterTest { FileStorageAdapter storage = new FileStorageAdapter(); storage.setFileStorage(file); - Optional elementOptional = storage.loadLastConfig(); - assertThat(elementOptional.isPresent(), is(false)); + List elementOptional = storage.loadLastConfigs(); + assertThat(elementOptional.size(), is(0)); } @Test(expected = NullPointerException.class) public void testNoProperties() throws Exception { FileStorageAdapter storage = new FileStorageAdapter(); - storage.loadLastConfig(); + storage.loadLastConfigs(); } @Test(expected = NullPointerException.class) diff --git a/opendaylight/config/logback-config/src/main/yang/config-logging.yang b/opendaylight/config/logback-config/src/main/yang/config-logging.yang index 7f4ea39dd4..3b28b57469 100644 --- a/opendaylight/config/logback-config/src/main/yang/config-logging.yang +++ b/opendaylight/config/logback-config/src/main/yang/config-logging.yang @@ -51,6 +51,7 @@ module config-logging { type string; mandatory true; } + key name; config:java-name-prefix FileAppenderTO; } @@ -89,6 +90,7 @@ module config-logging { type string; mandatory true; } + key name; leaf file-name-pattern { type string; @@ -107,7 +109,7 @@ module config-logging { leaf clean-history-on-start { type boolean; - default 0; + default false; } config:java-name-prefix RollingFileAppenderTO; } @@ -128,6 +130,8 @@ module config-logging { type string; mandatory true; } + key name; + config:java-name-prefix ConsoleAppenderTO; } @@ -136,6 +140,7 @@ module config-logging { type string; mandatory true; } + key logger-name; leaf level { type string; diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersister.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersister.java index b0b6f3d74c..f721895921 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersister.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersister.java @@ -7,19 +7,9 @@ */ package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Pattern; - +import com.google.common.annotations.VisibleForTesting; +import freemarker.template.Configuration; +import freemarker.template.Template; import org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator; import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.AnnotationsDirective; import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.ConstructorsDirective; @@ -33,11 +23,18 @@ import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; - -import freemarker.template.Configuration; -import freemarker.template.Template; -import freemarker.template.TemplateException; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; public class FtlFilePersister { private static final Logger logger = LoggerFactory @@ -56,7 +53,7 @@ public class FtlFilePersister { ftlFile.getFtlTempleteLocation()); try { template.process(ftlFile, writer); - } catch (TemplateException e) { + } catch (Throwable e) { throw new IllegalStateException( "Template error while generating " + ftlFile, e); } diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java index 3a6ff18081..115bb85b61 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java @@ -21,13 +21,13 @@ import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc; import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute.Dependency; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.Dependency; import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.VoidAttribute; import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation; import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation.Parameter; @@ -39,6 +39,7 @@ import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Meth import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper; import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil; +import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType; import org.opendaylight.yangtools.sal.binding.model.api.Type; import javax.management.openmbean.SimpleType; @@ -112,8 +113,8 @@ public class TemplateFactory { // convert attributes to getters for (AttributeIfc attributeIfc : entry.getAttributes()) { - String returnType = null; - returnType = getReturnType(entry, attributeIfc); + String returnType; + returnType = getReturnType(attributeIfc); String getterName = "get" + attributeIfc.getUpperCaseCammelCase(); MethodDeclaration getter = new MethodDeclaration(returnType, @@ -128,11 +129,11 @@ public class TemplateFactory { for (JavaAttribute ja : rpc.getParameters()) { Field field = new Field(Collections. emptyList(), ja.getType().getFullyQualifiedName(), - ja.getLowerCaseCammelCase()); + ja.getLowerCaseCammelCase(), ja.getNullableDefaultWrappedForCode()); fields.add(field); } MethodDeclaration operation = new MethodDeclaration( - getReturnType(entry, rpc.getReturnType()), rpc.getName(), fields); + getReturnType(rpc.getReturnType()), rpc.getName(), fields); methods.add(operation); } @@ -150,28 +151,35 @@ public class TemplateFactory { return result; } - private static String getReturnType(RuntimeBeanEntry entry, AttributeIfc attributeIfc) { + // FIXME: put into Type.toString + static String serializeType(Type type) { + if (type instanceof ParameterizedType){ + ParameterizedType parameterizedType = (ParameterizedType) type; + StringBuffer sb = new StringBuffer(); + sb.append(parameterizedType.getRawType().getFullyQualifiedName()); + sb.append("<"); + boolean first = true; + for(Type parameter: parameterizedType.getActualTypeArguments()) { + if (first) { + first = false; + } else { + sb.append(","); + } + sb.append(serializeType(parameter)); + } + sb.append(">"); + return sb.toString(); + } else { + return type.getFullyQualifiedName(); + } + } + + + private static String getReturnType(AttributeIfc attributeIfc) { String returnType; if (attributeIfc instanceof TypedAttribute) { - returnType = ((TypedAttribute) attributeIfc).getType() - .getFullyQualifiedName(); - } else if (attributeIfc instanceof TOAttribute) { - String fullyQualifiedName = FullyQualifiedNameHelper - .getFullyQualifiedName(entry.getPackageName(), - attributeIfc.getUpperCaseCammelCase()); - - returnType = fullyQualifiedName; - } else if (attributeIfc instanceof ListAttribute) { - AttributeIfc innerAttr = ((ListAttribute) attributeIfc) - .getInnerAttribute(); - - String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr) - .getType().getFullyQualifiedName() - : FullyQualifiedNameHelper.getFullyQualifiedName( - entry.getPackageName(), - attributeIfc.getUpperCaseCammelCase()); - - returnType = "java.util.List<" + innerTpe + ">"; + Type type = ((TypedAttribute) attributeIfc).getType(); + returnType = serializeType(type); } else if (attributeIfc == VoidAttribute.getInstance()) { return "void"; } else { @@ -292,8 +300,7 @@ public class TemplateFactory { public static GeneralInterfaceTemplate mXBeanInterfaceTemplateFromMbe( ModuleMXBeanEntry mbe) { MXBeanInterfaceAttributesProcessor attrProcessor = new MXBeanInterfaceAttributesProcessor(); - attrProcessor.processAttributes(mbe.getAttributes(), - mbe.getPackageName()); + attrProcessor.processAttributes(mbe.getAttributes()); GeneralInterfaceTemplate ifcTemplate = new GeneralInterfaceTemplate( getHeaderFromEntry(mbe), mbe.getPackageName(), mbe.getMXBeanInterfaceName(), Lists. newArrayList(), @@ -306,7 +313,7 @@ public class TemplateFactory { ModuleMXBeanEntry mbe) { Map retVal = Maps.newHashMap(); TOAttributesProcessor processor = new TOAttributesProcessor(); - processor.processAttributes(mbe.getAttributes(), mbe.getPackageName()); + processor.processAttributes(mbe.getAttributes()); for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor .getTOs()) { List constructors = Lists.newArrayList(); @@ -345,7 +352,7 @@ public class TemplateFactory { yangPropertiesToTypesMap.put(returnType.getAttributeYangName(), returnType); } - processor.processAttributes(yangPropertiesToTypesMap, rbe.getPackageName()); + processor.processAttributes(yangPropertiesToTypesMap); for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor .getTOs()) { List constructors = Lists.newArrayList(); @@ -372,36 +379,29 @@ public class TemplateFactory { private final List tos = Lists.newArrayList(); - void processAttributes(Map attributes, - String packageName) { + void processAttributes(Map attributes) { for (Entry attrEntry : attributes.entrySet()) { AttributeIfc attributeIfc = attrEntry.getValue(); if (attributeIfc instanceof TOAttribute) { - createTOInternal(packageName, attributeIfc); + createTOInternal((TOAttribute) attributeIfc); } if (attributeIfc instanceof ListAttribute) { AttributeIfc innerAttr = ((ListAttribute) attributeIfc) .getInnerAttribute(); if (innerAttr instanceof TOAttribute) { - createTOInternal(packageName, innerAttr); + createTOInternal((TOAttribute) innerAttr); } } } } - private void createTOInternal(String packageName, - AttributeIfc attributeIfc) { - String fullyQualifiedName = FullyQualifiedNameHelper - .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase()); + private void createTOInternal(TOAttribute toAttribute) { - String type = fullyQualifiedName; - String name = attributeIfc.getUpperCaseCammelCase(); - Map attrs = ((TOAttribute) attributeIfc) - .getCapitalizedPropertiesToTypesMap(); - // recursive processing - processAttributes(attrs, packageName); + Map attrs = toAttribute.getCapitalizedPropertiesToTypesMap(); + // recursive processing of TO's attributes + processAttributes(attrs); - tos.add(new TOInternal(type, name, attrs, packageName)); + tos.add(new TOInternal(toAttribute.getType(), attrs)); } List getTOs() { @@ -409,20 +409,22 @@ public class TemplateFactory { } private static class TOInternal { - private final String type, name; + private final String fullyQualifiedName, name; private List fields; private List methods; - public TOInternal(String type, String name, + public TOInternal(Type type, Map attrs) { + this(type.getFullyQualifiedName(), type.getName(), attrs, type.getPackageName()); + } + + public TOInternal(String fullyQualifiedName, String name, Map attrs, String packageName) { - super(); - this.type = type; + this.fullyQualifiedName = fullyQualifiedName; this.name = name; processAttrs(attrs, packageName); } - private void processAttrs(Map attrs, - String packageName) { + private void processAttrs(Map attrs, String packageName) { fields = Lists.newArrayList(); methods = Lists.newArrayList(); @@ -431,26 +433,18 @@ public class TemplateFactory { String varName = BindingGeneratorUtil .parseToValidParamName(attrEntry.getKey()); - String fullyQualifiedName = null; + String fullyQualifiedName, nullableDefault = null; if (attrEntry.getValue() instanceof TypedAttribute) { - Type innerType = ((TypedAttribute) attrEntry.getValue()) - .getType(); - fullyQualifiedName = innerType.getFullyQualifiedName(); - } else if (attrEntry.getValue() instanceof ListAttribute) { - AttributeIfc innerAttr = ((ListAttribute) attrEntry - .getValue()).getInnerAttribute(); - - String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr) - .getType().getFullyQualifiedName() - : FullyQualifiedNameHelper - .getFullyQualifiedName(packageName, attrEntry.getValue().getUpperCaseCammelCase()); - - fullyQualifiedName = "java.util.List<" + innerTpe + ">"; - } else + Type type = ((TypedAttribute) attrEntry.getValue()).getType(); + fullyQualifiedName = serializeType(type); + if(attrEntry.getValue() instanceof JavaAttribute) { + nullableDefault = ((JavaAttribute)attrEntry.getValue()).getNullableDefaultWrappedForCode(); + } + } else { fullyQualifiedName = FullyQualifiedNameHelper .getFullyQualifiedName(packageName, attrEntry.getValue().getUpperCaseCammelCase()); - - fields.add(new Field(fullyQualifiedName, varName)); + } + fields.add(new Field(fullyQualifiedName, varName, nullableDefault)); String getterName = "get" + innerName; MethodDefinition getter = new MethodDefinition( @@ -470,7 +464,7 @@ public class TemplateFactory { } String getType() { - return type; + return fullyQualifiedName; } String getName() { @@ -488,38 +482,16 @@ public class TemplateFactory { } private static class MXBeanInterfaceAttributesProcessor { - private static final String STRING_FULLY_QUALIFIED_NAME = "java.util.List"; private final List methods = Lists.newArrayList(); - void processAttributes(Map attributes, - String packageName) { + void processAttributes(Map attributes) { for (Entry attrEntry : attributes.entrySet()) { String returnType; AttributeIfc attributeIfc = attrEntry.getValue(); if (attributeIfc instanceof TypedAttribute) { - returnType = ((TypedAttribute) attributeIfc).getType() - .getFullyQualifiedName(); - } else if (attributeIfc instanceof TOAttribute) { - String fullyQualifiedName = FullyQualifiedNameHelper - .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase()); - - returnType = fullyQualifiedName; - } else if (attributeIfc instanceof ListAttribute) { - String fullyQualifiedName = null; - - AttributeIfc innerAttr = ((ListAttribute) attributeIfc) - .getInnerAttribute(); - if (innerAttr instanceof JavaAttribute) { - fullyQualifiedName = ((JavaAttribute) innerAttr) - .getType().getFullyQualifiedName(); - } else if (innerAttr instanceof TOAttribute) { - fullyQualifiedName = FullyQualifiedNameHelper - .getFullyQualifiedName(packageName, innerAttr.getUpperCaseCammelCase()); - } - - returnType = STRING_FULLY_QUALIFIED_NAME.concat("<") - .concat(fullyQualifiedName).concat(">"); + TypedAttribute typedAttribute = (TypedAttribute) attributeIfc; + returnType = serializeType(typedAttribute.getType()); } else { throw new UnsupportedOperationException( "Attribute not supported: " @@ -562,23 +534,25 @@ public class TemplateFactory { String packageName) { for (Entry attrEntry : attributes.entrySet()) { String type; + String nullableDefaultWrapped = null; AttributeIfc attributeIfc = attrEntry.getValue(); if (attributeIfc instanceof TypedAttribute) { - type = ((TypedAttribute) attributeIfc).getType() - .getFullyQualifiedName(); + TypedAttribute typedAttribute = (TypedAttribute) attributeIfc; + type = serializeType(typedAttribute.getType()); } else if (attributeIfc instanceof TOAttribute) { String fullyQualifiedName = FullyQualifiedNameHelper .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase()); type = fullyQualifiedName; - } else if (attributeIfc instanceof ListAttribute) { + } else if (attributeIfc instanceof ListAttribute) { //FIXME: listAttribute might extend TypedAttribute String fullyQualifiedName = null; AttributeIfc innerAttr = ((ListAttribute) attributeIfc) .getInnerAttribute(); if (innerAttr instanceof JavaAttribute) { fullyQualifiedName = ((JavaAttribute) innerAttr) .getType().getFullyQualifiedName(); + nullableDefaultWrapped = ((JavaAttribute) innerAttr).getNullableDefaultWrappedForCode(); } else if (innerAttr instanceof TOAttribute) { fullyQualifiedName = FullyQualifiedNameHelper .getFullyQualifiedName(packageName, innerAttr.getUpperCaseCammelCase()); @@ -594,7 +568,7 @@ public class TemplateFactory { } fields.add(new Field(type, attributeIfc - .getUpperCaseCammelCase())); + .getUpperCaseCammelCase(), nullableDefaultWrapped)); } } @@ -613,12 +587,16 @@ public class TemplateFactory { void processAttributes(Map attributes, String packageName) { for (Entry attrEntry : attributes.entrySet()) { - String type; + String type, nullableDefaultWrapped = null; AttributeIfc attributeIfc = attrEntry.getValue(); if (attributeIfc instanceof TypedAttribute) { - type = ((TypedAttribute) attributeIfc).getType() - .getFullyQualifiedName(); + TypedAttribute typedAttribute = (TypedAttribute) attributeIfc; + type = serializeType(typedAttribute.getType()); + if (attributeIfc instanceof JavaAttribute) { + nullableDefaultWrapped = ((JavaAttribute) attributeIfc).getNullableDefaultWrappedForCode(); + } + } else if (attributeIfc instanceof TOAttribute) { String fullyQualifiedName = FullyQualifiedNameHelper .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase()); @@ -631,6 +609,7 @@ public class TemplateFactory { if (innerAttr instanceof JavaAttribute) { fullyQualifiedName = ((JavaAttribute) innerAttr) .getType().getFullyQualifiedName(); + nullableDefaultWrapped = ((JavaAttribute) innerAttr).getNullableDefaultWrappedForCode(); } else if (innerAttr instanceof TOAttribute) { fullyQualifiedName = FullyQualifiedNameHelper .getFullyQualifiedName(packageName, innerAttr.getUpperCaseCammelCase()); @@ -651,9 +630,9 @@ public class TemplateFactory { List annotations = Lists .newArrayList(overrideAnnotation); - if (attributeIfc instanceof DependencyAttribute) { + if (attributeIfc instanceof AbstractDependencyAttribute) { isDependency = true; - dependency = ((DependencyAttribute) attributeIfc) + dependency = ((AbstractDependencyAttribute) attributeIfc) .getDependency(); annotations.add(Annotation .createRequireIfcAnnotation(dependency.getSie())); @@ -662,8 +641,7 @@ public class TemplateFactory { String varName = BindingGeneratorUtil .parseToValidParamName(attrEntry.getKey()); moduleFields.add(new ModuleField(type, varName, attributeIfc - .getUpperCaseCammelCase(), attributeIfc - .getNullableDefault(), isDependency, dependency)); + .getUpperCaseCammelCase(), nullableDefaultWrapped, isDependency, dependency)); String getterName = "get" + attributeIfc.getUpperCaseCammelCase(); diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Field.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Field.java index fe9e885b6f..0857ec6f8d 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Field.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Field.java @@ -7,10 +7,10 @@ */ package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model; -import java.util.List; - import com.google.common.collect.Lists; +import java.util.List; + public class Field { private final String type; private final String name; @@ -21,6 +21,10 @@ public class Field { this(Lists. newArrayList(), type, name, null); } + public Field(String type, String name, String definition) { + this(Lists. newArrayList(), type, name, definition); + } + public Field(List modifiers, String type, String name) { this(modifiers, type, name, null); } diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/ModuleField.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/ModuleField.java index 293696d10e..5624e169da 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/ModuleField.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/ModuleField.java @@ -7,11 +7,13 @@ */ package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model; + +import org.opendaylight.controller.config.yangjmxgenerator.attribute.Dependency; + +import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute.Dependency; - public class ModuleField extends Field { private final String nullableDefault, attributeName; @@ -22,10 +24,14 @@ public class ModuleField extends Field { String attributeName, String nullableDefault, boolean isDependency, Dependency dependency) { super(modifiers, type, name); - this.nullableDefault = nullableDefault; this.dependent = isDependency; this.dependency = dependency; this.attributeName = attributeName; + if (type.startsWith(List.class.getName()) && nullableDefault == null) { + String generics = type.substring(List.class.getName().length()); + nullableDefault = "new " + ArrayList.class.getName() + generics + "()"; + } + this.nullableDefault = nullableDefault; } public ModuleField(String type, String name, String attributeName, @@ -49,4 +55,5 @@ public class ModuleField extends Field { public String getAttributeName() { return attributeName; } + } diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_abs_template_new.ftl b/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_abs_template_new.ftl index d0646f467a..f7197d1582 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_abs_template_new.ftl +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_abs_template_new.ftl @@ -52,8 +52,14 @@ package ${packageName}; public void validate(){ <#list moduleFields as field> <#if field.dependent==true && field.dependency.mandatory==true> + <#if field.type?starts_with("java.util.List")> + for(javax.management.ObjectName dep : ${field.name}) { + dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, dep, ${field.name}JmxAttribute); + } + <#else> dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, ${field.name}, ${field.name}JmxAttribute); + customValidation(); } @@ -65,10 +71,17 @@ package ${packageName}; // caches of resolved dependencies <#list moduleFields as field> <#if field.dependent==true> + <#if field.type?starts_with("java.util.List")> + private java.util.List<${field.dependency.sie.exportedOsgiClassName}> ${field.name}Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>(); + protected final java.util.List<${field.dependency.sie.exportedOsgiClassName}> get${field.attributeName}Dependency(){ + return ${field.name}Dependency; + } + <#else> private ${field.dependency.sie.exportedOsgiClassName} ${field.name}Dependency; protected final ${field.dependency.sie.exportedOsgiClassName} get${field.attributeName}Dependency(){ return ${field.name}Dependency; } + @@ -79,12 +92,18 @@ package ${packageName}; <#list moduleFields as field> <#if field.dependent==true> - <#if field.dependency.mandatory==false> if(${field.name}!=null) { - ${field.name}Dependency = dependencyResolver.resolveInstance(${field.dependency.sie.exportedOsgiClassName}.class, ${field.name}, ${field.name}JmxAttribute); + <#if field.type?starts_with("java.util.List")> + ${field.name}Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>(); + for(javax.management.ObjectName dep : ${field.name}) { + ${field.name}Dependency.add(dependencyResolver.resolveInstance(${field.dependency.sie.exportedOsgiClassName}.class, dep, ${field.name}JmxAttribute)); + } + <#else> + ${field.name}Dependency = dependencyResolver.resolveInstance(${field.dependency.sie.exportedOsgiClassName}.class, ${field.name}, ${field.name}JmxAttribute); + <#if field.dependency.mandatory==false> } diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java index 0d6ec3cccb..1945cac1c2 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java @@ -75,6 +75,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +//TODO: refactor public class JMXGeneratorTest extends AbstractGeneratorTest { JMXGenerator jmxGenerator; @@ -84,19 +85,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { File generatedResourcesDir; private static final List expectedModuleFileNames = ServiceInterfaceEntryTest - .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, " + - "AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, " + - "AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, " + - "AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, " + - "AsyncEventBusModule.java, AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, " + - "AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, " + - "AsyncEventBusRuntimeRegistrator.java, DynamicThreadPoolModule.java, " + - "DynamicThreadPoolModuleFactory.java, DynamicThreadPoolModuleMXBean.java, " + - "DynamicThreadPoolRuntimeMXBean.java, DynamicThreadPoolRuntimeRegistration.java, " + - "DynamicThreadPoolRuntimeRegistrator.java, EventBusModule.java, EventBusModuleFactory.java, " + - "EventBusModuleMXBean.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, " + - "InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, " + - "NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, Peer.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]"); + .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, AbstractThreadPoolRegistryImplModule.java, AbstractThreadPoolRegistryImplModuleFactory.java, AsyncEventBusModule.java, AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, AsyncEventBusRuntimeRegistrator.java, DynamicThreadPoolModule.java, DynamicThreadPoolModuleFactory.java, DynamicThreadPoolModuleMXBean.java, DynamicThreadPoolRuntimeMXBean.java, DynamicThreadPoolRuntimeRegistration.java, DynamicThreadPoolRuntimeRegistrator.java, EventBusModule.java, EventBusModuleFactory.java, EventBusModuleMXBean.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, Peer.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, ThreadPoolRegistryImplModule.java, ThreadPoolRegistryImplModuleFactory.java, ThreadPoolRegistryImplModuleMXBean.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]"); private static final List expectedBGPNames = ServiceInterfaceEntryTest .toFileNames("[AbstractBgpListenerImplModule.java, " + "AbstractBgpListenerImplModuleFactory.java, " + @@ -119,38 +108,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { "NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, " + "NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java]"); private static final List expectedAllFileNames = ServiceInterfaceEntryTest - .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, " + - "AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, " + - "AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, " + - "AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, " + - "AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, " + - "AbstractNetconfTestFileImplModule.java, AbstractNetconfTestFileImplModuleFactory.java, " + - "AbstractNetconfTestFiles1ImplModule.java, AbstractNetconfTestFiles1ImplModuleFactory.java, " + - "AbstractNetconfTestImplModule.java, AbstractNetconfTestImplModuleFactory.java, " + - "AbstractTestFileImplModule.java, AbstractTestFileImplModuleFactory.java, " + - "AbstractTestFiles1ImplModule.java, AbstractTestFiles1ImplModuleFactory.java, " + - "AbstractTestImplModule.java, AbstractTestImplModuleFactory.java, AsyncEventBusModule.java, " + - "AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, " + - "AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, " + - "AsyncEventBusRuntimeRegistrator.java, AutoCloseableServiceInterface.java, " + - "BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java," + - " BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, " + - "BgpListenerImplRuntimeRegistrator.java, ComplexDtoBInner.java, ComplexList.java, Deep.java, " + - "DtoA.java, DtoA.java, DtoA.java, DtoA1.java, DtoAInner.java, DtoAInnerInner.java, DtoB.java, " + - "DtoC.java, DynamicThreadPoolModule.java, DynamicThreadPoolModuleFactory.java, " + - "DynamicThreadPoolModuleMXBean.java, DynamicThreadPoolRuntimeMXBean.java, " + - "DynamicThreadPoolRuntimeRegistration.java, DynamicThreadPoolRuntimeRegistrator.java, " + - "EventBusModule.java, EventBusModuleFactory.java, EventBusModuleMXBean.java, " + - "EventBusServiceInterface.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, " + - "InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, " + - "NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, " + - "NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, " + - "NetconfTestFileImplModule.java, NetconfTestFileImplModuleFactory.java, " + - "NetconfTestFileImplModuleMXBean.java, NetconfTestFileImplRuntimeMXBean.java, " + - "NetconfTestFileImplRuntimeRegistration.java, NetconfTestFileImplRuntimeRegistrator.java, " + - "NetconfTestFiles1ImplModule.java, NetconfTestFiles1ImplModuleFactory.java, " + - "NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, " + - "NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, NetconfTestImplModule.java, NetconfTestImplModuleFactory.java, NetconfTestImplModuleMXBean.java, NetconfTestImplRuntimeMXBean.java, NetconfTestImplRuntimeRegistration.java, NetconfTestImplRuntimeRegistrator.java, Peer.java, Peer.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java, ScheduledThreadPoolServiceInterface.java, SimpleList.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java, TestImplModule.java, TestImplModuleFactory.java, TestImplModuleMXBean.java, TestImplRuntimeMXBean.java, TestImplRuntimeRegistration.java, TestImplRuntimeRegistrator.java, ThreadFactoryServiceInterface.java, ThreadPoolServiceInterface.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]"); + .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, AbstractNetconfTestFileImplModule.java, AbstractNetconfTestFileImplModuleFactory.java, AbstractNetconfTestFiles1ImplModule.java, AbstractNetconfTestFiles1ImplModuleFactory.java, AbstractNetconfTestImplModule.java, AbstractNetconfTestImplModuleFactory.java, AbstractTestFileImplModule.java, AbstractTestFileImplModuleFactory.java, AbstractTestFiles1ImplModule.java, AbstractTestFiles1ImplModuleFactory.java, AbstractTestImplModule.java, AbstractTestImplModuleFactory.java, AbstractThreadPoolRegistryImplModule.java, AbstractThreadPoolRegistryImplModuleFactory.java, AsyncEventBusModule.java, AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, AsyncEventBusRuntimeRegistrator.java, AutoCloseableServiceInterface.java, BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java, BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, BgpListenerImplRuntimeRegistrator.java, ComplexDtoBInner.java, ComplexList.java, Deep.java, DtoA.java, DtoA.java, DtoA.java, DtoA1.java, DtoAInner.java, DtoAInnerInner.java, DtoB.java, DtoC.java, DynamicThreadPoolModule.java, DynamicThreadPoolModuleFactory.java, DynamicThreadPoolModuleMXBean.java, DynamicThreadPoolRuntimeMXBean.java, DynamicThreadPoolRuntimeRegistration.java, DynamicThreadPoolRuntimeRegistrator.java, EventBusModule.java, EventBusModuleFactory.java, EventBusModuleMXBean.java, EventBusServiceInterface.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, NetconfTestFileImplModule.java, NetconfTestFileImplModuleFactory.java, NetconfTestFileImplModuleMXBean.java, NetconfTestFileImplRuntimeMXBean.java, NetconfTestFileImplRuntimeRegistration.java, NetconfTestFileImplRuntimeRegistrator.java, NetconfTestFiles1ImplModule.java, NetconfTestFiles1ImplModuleFactory.java, NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, NetconfTestImplModule.java, NetconfTestImplModuleFactory.java, NetconfTestImplModuleMXBean.java, NetconfTestImplRuntimeMXBean.java, NetconfTestImplRuntimeRegistration.java, NetconfTestImplRuntimeRegistrator.java, Peer.java, Peer.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java, ScheduledThreadPoolServiceInterface.java, SimpleList.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java, TestImplModule.java, TestImplModuleFactory.java, TestImplModuleMXBean.java, TestImplRuntimeMXBean.java, TestImplRuntimeRegistration.java, TestImplRuntimeRegistrator.java, ThreadFactoryServiceInterface.java, ThreadPoolRegistryImplModule.java, ThreadPoolRegistryImplModuleFactory.java, ThreadPoolRegistryImplModuleMXBean.java, ThreadPoolServiceInterface.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]"); private static final List expectedGenerateMBEsListNames = ServiceInterfaceEntryTest .toFileNames("[AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java, BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, BgpListenerImplRuntimeRegistrator.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java]"); @@ -381,7 +339,11 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + ".threads.java.DynamicThreadPoolModuleFactory",// PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX - + ".threads.java.NamingThreadFactoryModuleFactory"); + + ".threads.java.NamingThreadFactoryModuleFactory", // + PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + + ".threads.java.ThreadPoolRegistryImplModuleFactory"); + + assertThat(lines, equalTo(expectedLines)); } @@ -572,7 +534,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { assertDeclaredField(fieldDeclarations, "private java.util.concurrent.ThreadFactory threadfactoryDependency"); assertDeclaredField(fieldDeclarations, - "private java.lang.Long keepAlive=10"); + "private java.lang.Long keepAlive=new java.lang.Long(\"10\")"); assertDeclaredField(fieldDeclarations, "private java.lang.Long coreSize"); assertDeclaredField(fieldDeclarations, "private byte[] binary"); diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryTemplatesTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryTemplatesTest.java index 3c47931896..48d5b30eb2 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryTemplatesTest.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryTemplatesTest.java @@ -7,14 +7,7 @@ */ package org.opendaylight.controller.config.yangjmxgenerator.plugin; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -import java.util.Collections; -import java.util.Map; - +import com.google.common.collect.Maps; import org.junit.Test; import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; @@ -23,7 +16,13 @@ import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractFa import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory; import org.opendaylight.yangtools.sal.binding.model.api.Type; -import com.google.common.collect.Maps; +import java.util.Collections; +import java.util.Map; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; public class ModuleMXBeanEntryTemplatesTest { @@ -61,6 +60,7 @@ public class ModuleMXBeanEntryTemplatesTest { doReturn("package.type").when(typeA).getFullyQualifiedName(); doReturn(typeA).when(attr).getType(); doReturn("Type").when(attr).getUpperCaseCammelCase(); + doReturn("new Default()").when(attr).getNullableDefault(); return attr; } diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java index 70a4edde41..4eba739b46 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java @@ -7,12 +7,27 @@ */ package org.opendaylight.controller.config.yangjmxgenerator; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Sets; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static java.lang.String.format; +import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.createConfigQName; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException; @@ -21,6 +36,7 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; @@ -37,20 +53,9 @@ import org.opendaylight.yangtools.yang.model.api.UsesNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static java.lang.String.format; -import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.createConfigQName; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.Sets; /** * Represents part of yang model that describes a module. @@ -180,8 +185,9 @@ public class ModuleMXBeanEntry extends AbstractEntry { } /** - * @return services implemented by this module. Keys are fully qualified java names of generated - * ServiceInterface classes, values are identity local names. + * @return services implemented by this module. Keys are fully qualified + * java names of generated ServiceInterface classes, values are + * identity local names. */ public Map getProvidedServices() { return providedServices; @@ -338,7 +344,7 @@ public class ModuleMXBeanEntry extends AbstractEntry { moduleLocalNameFromXPath); yangToAttributes = fillConfiguration(choiceCaseNode, currentModule, typeProviderWrapper, - qNamesToSIEs, schemaContext); + qNamesToSIEs, schemaContext, packageName); checkUniqueAttributesWithGeneratedClass( uniqueGeneratedClassesNames, when.getQName(), yangToAttributes); @@ -356,7 +362,6 @@ public class ModuleMXBeanEntry extends AbstractEntry { e.getConflictingName(), when.getQName(), when.getQName()); } - checkUniqueRuntimeBeansGeneratedClasses( uniqueGeneratedClassesNames, when, runtimeBeans); Set runtimeBeanEntryValues = Sets @@ -411,6 +416,11 @@ public class ModuleMXBeanEntry extends AbstractEntry { . emptyList()); } } + // check attributes name uniqueness + for (Entry entry : result.entrySet()) { + checkUniqueRuntimeBeanAttributesName(entry.getValue(), + uniqueGeneratedClassesNames); + } if (unaugmentedModuleIdentities.size() > 0) { logger.warn("Augmentation not found for all module identities: {}", unaugmentedModuleIdentities.keySet()); @@ -439,6 +449,25 @@ public class ModuleMXBeanEntry extends AbstractEntry { } } + private static void checkUniqueRuntimeBeanAttributesName( + ModuleMXBeanEntry mxBeanEntry, + Map uniqueGeneratedClassesNames) { + for (RuntimeBeanEntry runtimeBeanEntry : mxBeanEntry.getRuntimeBeans()) { + for (String runtimeAttName : runtimeBeanEntry + .getYangPropertiesToTypesMap().keySet()) { + if (mxBeanEntry.getAttributes().keySet() + .contains(runtimeAttName)) { + QName qName1 = uniqueGeneratedClassesNames + .get(runtimeBeanEntry.getJavaNameOfRuntimeMXBean()); + QName qName2 = uniqueGeneratedClassesNames.get(mxBeanEntry + .getGloballyUniqueName()); + throw new NameConflictException(runtimeAttName, qName1, + qName2); + } + } + } + } + private static void checkUniqueAttributesWithGeneratedClass( Map uniqueGeneratedClassNames, QName parentQName, Map yangToAttributes) { @@ -484,12 +513,12 @@ public class ModuleMXBeanEntry extends AbstractEntry { ChoiceCaseNode choiceCaseNode, Module currentModule, TypeProviderWrapper typeProviderWrapper, Map qNamesToSIEs, - SchemaContext schemaContext) { + SchemaContext schemaContext, String packageName) { Map yangToAttributes = new HashMap<>(); for (DataSchemaNode attrNode : choiceCaseNode.getChildNodes()) { AttributeIfc attributeValue = getAttributeValue(attrNode, currentModule, qNamesToSIEs, typeProviderWrapper, - schemaContext); + schemaContext, packageName); yangToAttributes.put(attributeValue.getAttributeYangName(), attributeValue); } @@ -549,11 +578,12 @@ public class ModuleMXBeanEntry extends AbstractEntry { } } - private static int getChildNodeSizeWithoutUses(ContainerSchemaNode csn) { + private static int getChildNodeSizeWithoutUses(DataNodeContainer csn) { int result = 0; for (DataSchemaNode dsn : csn.getChildNodes()) { - if (dsn.isAddedByUses() == false) + if (dsn.isAddedByUses() == false) { result++; + } } return result; } @@ -561,7 +591,8 @@ public class ModuleMXBeanEntry extends AbstractEntry { private static AttributeIfc getAttributeValue(DataSchemaNode attrNode, Module currentModule, Map qNamesToSIEs, - TypeProviderWrapper typeProviderWrapper, SchemaContext schemaContext) { + TypeProviderWrapper typeProviderWrapper, + SchemaContext schemaContext, String packageName) { if (attrNode instanceof LeafSchemaNode) { // simple type @@ -570,49 +601,80 @@ public class ModuleMXBeanEntry extends AbstractEntry { } else if (attrNode instanceof ContainerSchemaNode) { // reference or TO ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode; - if (containerSchemaNode.getUses().size() == 1 - && getChildNodeSizeWithoutUses(containerSchemaNode) == 0) { - // reference - UsesNode usesNode = containerSchemaNode.getUses().iterator() - .next(); - checkState(usesNode.getRefines().size() == 1, - "Unexpected 'refine' child node size of " - + containerSchemaNode); - LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines() - .values().iterator().next(); - checkState(refine.getUnknownSchemaNodes().size() == 1, - "Unexpected unknown schema node size of " + refine); - UnknownSchemaNode requiredIdentity = refine - .getUnknownSchemaNodes().iterator().next(); - checkState( - ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity - .getNodeType()), - "Unexpected language extension " + requiredIdentity); - String prefixAndIdentityLocalName = requiredIdentity - .getNodeParameter(); - // import should point to a module - ServiceInterfaceEntry serviceInterfaceEntry = findSIE( - prefixAndIdentityLocalName, currentModule, - qNamesToSIEs, schemaContext); - boolean mandatory = refine.getConstraints().isMandatory(); - return new DependencyAttribute(attrNode, serviceInterfaceEntry, - mandatory, attrNode.getDescription()); + Optional dependencyAttributeOptional = extractDependency( + containerSchemaNode, attrNode, currentModule, qNamesToSIEs, + schemaContext); + if (dependencyAttributeOptional.isPresent()) { + return dependencyAttributeOptional.get(); } else { return TOAttribute.create(containerSchemaNode, - typeProviderWrapper); + typeProviderWrapper, packageName); } + } else if (attrNode instanceof LeafListSchemaNode) { return ListAttribute.create((LeafListSchemaNode) attrNode, typeProviderWrapper); } else if (attrNode instanceof ListSchemaNode) { - return ListAttribute.create((ListSchemaNode) attrNode, - typeProviderWrapper); + ListSchemaNode listSchemaNode = (ListSchemaNode) attrNode; + Optional dependencyAttributeOptional = extractDependency( + listSchemaNode, attrNode, currentModule, qNamesToSIEs, + schemaContext); + if (dependencyAttributeOptional.isPresent()) { + return dependencyAttributeOptional.get(); + } else { + return ListAttribute.create(listSchemaNode, + typeProviderWrapper, packageName); + } } else { throw new UnsupportedOperationException( "Unknown configuration node " + attrNode.toString()); } } + private static Optional extractDependency( + DataNodeContainer dataNodeContainer, DataSchemaNode attrNode, + Module currentModule, + Map qNamesToSIEs, + SchemaContext schemaContext) { + if (dataNodeContainer.getUses().size() == 1 + && getChildNodeSizeWithoutUses(dataNodeContainer) == 0) { + // reference + UsesNode usesNode = dataNodeContainer.getUses().iterator().next(); + checkState(usesNode.getRefines().size() == 1, + "Unexpected 'refine' child node size of " + + dataNodeContainer); + LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines() + .values().iterator().next(); + checkState(refine.getUnknownSchemaNodes().size() == 1, + "Unexpected unknown schema node size of " + refine); + UnknownSchemaNode requiredIdentity = refine.getUnknownSchemaNodes() + .iterator().next(); + checkState( + ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity + .getNodeType()), "Unexpected language extension " + + requiredIdentity); + String prefixAndIdentityLocalName = requiredIdentity + .getNodeParameter(); + // import should point to a module + ServiceInterfaceEntry serviceInterfaceEntry = findSIE( + prefixAndIdentityLocalName, currentModule, qNamesToSIEs, + schemaContext); + boolean mandatory = refine.getConstraints().isMandatory(); + AbstractDependencyAttribute reference; + if (dataNodeContainer instanceof ContainerSchemaNode) { + reference = new DependencyAttribute(attrNode, + serviceInterfaceEntry, mandatory, + attrNode.getDescription()); + } else { + reference = new ListDependenciesAttribute(attrNode, + serviceInterfaceEntry, mandatory, + attrNode.getDescription()); + } + return Optional.of(reference); + } + return Optional.absent(); + } + private static ServiceInterfaceEntry findSIE( String prefixAndIdentityLocalName, Module currentModule, Map qNamesToSIEs, diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java index 9b82f123ca..f19a46d0f4 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java @@ -67,7 +67,7 @@ public class RuntimeBeanEntry { private final Set rpcs; @VisibleForTesting - public RuntimeBeanEntry(String packageName, + RuntimeBeanEntry(String packageName, DataSchemaNode nodeForReporting, String yangName, String javaNamePrefix, boolean isRoot, Optional keyYangName, List attributes, @@ -228,7 +228,7 @@ public class RuntimeBeanEntry { ContainerSchemaNode container = (ContainerSchemaNode) child; // this can be either TO or hierarchical RB TOAttribute toAttribute = TOAttribute.create(container, - typeProviderWrapper); + typeProviderWrapper, packageName); attributes.add(toAttribute); } else if (child instanceof ListSchemaNode) { if (isInnerStateBean(child)) { @@ -239,7 +239,7 @@ public class RuntimeBeanEntry { runtimeBeanEntries.add(hierarchicalChild); } else /* ordinary list attribute */{ ListAttribute listAttribute = ListAttribute.create( - (ListSchemaNode) child, typeProviderWrapper); + (ListSchemaNode) child, typeProviderWrapper, packageName); attributes.add(listAttribute); } @@ -280,7 +280,7 @@ public class RuntimeBeanEntry { } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) { DataSchemaNode returnDSN = rpcDefinition.getOutput() .getChildNodes().iterator().next(); - returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper); + returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper, packageName); } else { throw new IllegalArgumentException( @@ -311,16 +311,17 @@ public class RuntimeBeanEntry { attributes, rpcs); } - private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper) { + private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper, + String packageName) { if (child instanceof LeafSchemaNode) { LeafSchemaNode leaf = (LeafSchemaNode) child; return new JavaAttribute(leaf, typeProviderWrapper); } else if (child instanceof ContainerSchemaNode) { ContainerSchemaNode container = (ContainerSchemaNode) child; - TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper); + TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper, packageName); return toAttribute; } else if (child instanceof ListSchemaNode) { - return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper); + return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper, packageName); } else if (child instanceof LeafListSchemaNode) { return ListAttribute.create((LeafListSchemaNode) child, typeProviderWrapper); } else { diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java index a2238d1a13..764ecd3886 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java @@ -26,6 +26,10 @@ public class TypeProviderWrapper { return getType(leaf, type); } + public String getDefault(LeafSchemaNode node) { + return typeProvider.getTypeDefaultConstruction(node); + } + public Type getType(SchemaNode leaf, TypeDefinition type) { Type javaType; try { diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractDependencyAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractDependencyAttribute.java new file mode 100644 index 0000000000..eb1c9e41a6 --- /dev/null +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractDependencyAttribute.java @@ -0,0 +1,86 @@ +/* + * 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.yangjmxgenerator.attribute; + +import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; + +public abstract class AbstractDependencyAttribute extends AbstractAttribute implements TypedAttribute { + + protected final Dependency dependency; + protected final String nullableDescription, nullableDefault; + + public AbstractDependencyAttribute(DataSchemaNode attrNode, + ServiceInterfaceEntry sie, boolean mandatory, + String nullableDescription) { + super(attrNode); + dependency = new Dependency(sie, mandatory); + this.nullableDescription = nullableDescription; + nullableDefault = null; + } + + public Dependency getDependency() { + return dependency; + } + + @Override + public String getNullableDescription() { + return nullableDescription; + } + + @Override + public String getNullableDefault() { + return nullableDefault; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; + + AbstractDependencyAttribute that = (AbstractDependencyAttribute) o; + + if (dependency != null ? !dependency.equals(that.dependency) + : that.dependency != null) + return false; + if (nullableDefault != null ? !nullableDefault + .equals(that.nullableDefault) : that.nullableDefault != null) + return false; + if (nullableDescription != null ? !nullableDescription + .equals(that.nullableDescription) + : that.nullableDescription != null) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (dependency != null ? dependency.hashCode() : 0); + result = 31 + * result + + (nullableDescription != null ? nullableDescription.hashCode() + : 0); + result = 31 * result + + (nullableDefault != null ? nullableDefault.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return getClass().getName() + "{" + getAttributeYangName() + "," + + "dependency=" + dependency + '}'; + } + + +} diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/Dependency.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/Dependency.java new file mode 100644 index 0000000000..38de6e1528 --- /dev/null +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/Dependency.java @@ -0,0 +1,45 @@ +package org.opendaylight.controller.config.yangjmxgenerator.attribute; + +import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry; + +public class Dependency { + private final ServiceInterfaceEntry sie; + private final boolean mandatory; + + public Dependency(ServiceInterfaceEntry sie, boolean mandatory) { + this.sie = sie; + this.mandatory = mandatory; + } + + public ServiceInterfaceEntry getSie() { + return sie; + } + + public boolean isMandatory() { + return mandatory; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + Dependency that = (Dependency) o; + + if (mandatory != that.mandatory) + return false; + if (!sie.equals(that.sie)) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = sie.hashCode(); + result = 31 * result + (mandatory ? 1 : 0); + return result; + } + } diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/DependencyAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/DependencyAttribute.java index 1912b75e0e..d8df78af0a 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/DependencyAttribute.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/DependencyAttribute.java @@ -15,19 +15,13 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import javax.management.ObjectName; import javax.management.openmbean.SimpleType; -public class DependencyAttribute extends AbstractAttribute implements - TypedAttribute { +public class DependencyAttribute extends AbstractDependencyAttribute { - private final Dependency dependency; - private final String nullableDescription, nullableDefault; public DependencyAttribute(DataSchemaNode attrNode, ServiceInterfaceEntry sie, boolean mandatory, String nullableDescription) { - super(attrNode); - dependency = new Dependency(sie, mandatory); - this.nullableDescription = nullableDescription; - nullableDefault = null; + super(attrNode, sie, mandatory, nullableDescription); } @Override @@ -35,109 +29,9 @@ public class DependencyAttribute extends AbstractAttribute implements return Types.typeForClass(ObjectName.class); } - public Dependency getDependency() { - return dependency; - } - - @Override - public String getNullableDescription() { - return nullableDescription; - } - - @Override - public String getNullableDefault() { - return nullableDefault; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - if (!super.equals(o)) - return false; - - DependencyAttribute that = (DependencyAttribute) o; - - if (dependency != null ? !dependency.equals(that.dependency) - : that.dependency != null) - return false; - if (nullableDefault != null ? !nullableDefault - .equals(that.nullableDefault) : that.nullableDefault != null) - return false; - if (nullableDescription != null ? !nullableDescription - .equals(that.nullableDescription) - : that.nullableDescription != null) - return false; - - return true; - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (dependency != null ? dependency.hashCode() : 0); - result = 31 - * result - + (nullableDescription != null ? nullableDescription.hashCode() - : 0); - result = 31 * result - + (nullableDefault != null ? nullableDefault.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "DependencyAttribute{" + getAttributeYangName() + "," - + "dependency=" + dependency + '}'; - } - @Override public SimpleType getOpenType() { return SimpleType.OBJECTNAME; } - public static class Dependency { - private final ServiceInterfaceEntry sie; - private final boolean mandatory; - - public Dependency(ServiceInterfaceEntry sie, boolean mandatory) { - this.sie = sie; - this.mandatory = mandatory; - } - - public ServiceInterfaceEntry getSie() { - return sie; - } - - public boolean isMandatory() { - return mandatory; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - Dependency that = (Dependency) o; - - if (mandatory != that.mandatory) - return false; - if (!sie.equals(that.sie)) - return false; - - return true; - } - - @Override - public int hashCode() { - int result = sie.hashCode(); - result = 31 * result + (mandatory ? 1 : 0); - return result; - } - } - } diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java index 325ca9ee06..3e20e4a55a 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java @@ -22,7 +22,7 @@ import javax.management.openmbean.SimpleType; public class JavaAttribute extends AbstractAttribute implements TypedAttribute { private final Type type; - private final String nullableDescription, nullableDefault; + private final String nullableDescription, nullableDefault, nullableDefaultWrappedForCode; private final TypeProviderWrapper typeProviderWrapper; private final TypeDefinition typeDefinition; @@ -33,6 +33,7 @@ public class JavaAttribute extends AbstractAttribute implements TypedAttribute { this.typeDefinition = leaf.getType(); this.typeProviderWrapper = typeProviderWrapper; this.nullableDefault = leaf.getDefault(); + this.nullableDefaultWrappedForCode = leaf.getDefault() == null ? null : typeProviderWrapper.getDefault(leaf); this.nullableDescription = leaf.getDescription(); } @@ -42,10 +43,14 @@ public class JavaAttribute extends AbstractAttribute implements TypedAttribute { this.type = typeProviderWrapper.getType(leaf); this.typeDefinition = leaf.getType(); this.typeProviderWrapper = typeProviderWrapper; - this.nullableDefault = null; + this.nullableDefault = nullableDefaultWrappedForCode = null; this.nullableDescription = leaf.getDescription(); } + public TypeDefinition getTypeDefinition() { + return typeDefinition; + } + /** * Returns the most base type */ @@ -56,6 +61,10 @@ public class JavaAttribute extends AbstractAttribute implements TypedAttribute { return baseType; } + public String getNullableDefaultWrappedForCode() { + return nullableDefaultWrappedForCode; + } + @Override public Type getType() { return type; diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListAttribute.java index 083b0b53e9..73b557e291 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListAttribute.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListAttribute.java @@ -7,24 +7,27 @@ */ package org.opendaylight.controller.config.yangjmxgenerator.attribute; -import javax.management.openmbean.ArrayType; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.OpenType; - import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper; +import org.opendaylight.yangtools.binding.generator.util.Types; +import org.opendaylight.yangtools.sal.binding.model.api.Type; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -public class ListAttribute extends AbstractAttribute { +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import java.util.List; + +public class ListAttribute extends AbstractAttribute implements TypedAttribute { private final String nullableDescription, nullableDefault; - private final AttributeIfc innerAttribute; + private final TypedAttribute innerAttribute; public static ListAttribute create(ListSchemaNode node, - TypeProviderWrapper typeProvider) { + TypeProviderWrapper typeProvider, String packageName) { - AttributeIfc innerAttribute = TOAttribute.create(node, typeProvider); + TOAttribute innerAttribute = TOAttribute.create(node, typeProvider, packageName); return new ListAttribute(node, innerAttribute, node.getDescription()); } @@ -32,12 +35,12 @@ public class ListAttribute extends AbstractAttribute { public static ListAttribute create(LeafListSchemaNode node, TypeProviderWrapper typeProvider) { - AttributeIfc innerAttribute = new JavaAttribute(node, typeProvider); + JavaAttribute innerAttribute = new JavaAttribute(node, typeProvider); return new ListAttribute(node, innerAttribute, node.getDescription()); } - ListAttribute(DataSchemaNode attrNode, AttributeIfc innerAttribute, + ListAttribute(DataSchemaNode attrNode, TypedAttribute innerAttribute, String description) { super(attrNode); this.nullableDescription = description; @@ -99,14 +102,24 @@ public class ListAttribute extends AbstractAttribute { return true; } + + @Override + public Type getType() { + return Types.parameterizedTypeFor(Types.typeForClass(List.class), innerAttribute.getType()); + } + @Override public ArrayType getOpenType() { - OpenType inerOpenType = innerAttribute.getOpenType(); + OpenType innerOpenType = innerAttribute.getOpenType(); + return constructArrayType(innerOpenType); + } + + static ArrayType constructArrayType(OpenType innerOpenType){ try { - return new ArrayType<>(1, inerOpenType); + return new ArrayType<>(1, innerOpenType); } catch (OpenDataException e) { throw new RuntimeException("Unable to create " + ArrayType.class - + " with inner element of type " + inerOpenType, e); + + " with inner element of type " + innerOpenType, e); } } diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListDependenciesAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListDependenciesAttribute.java new file mode 100644 index 0000000000..641099aef3 --- /dev/null +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListDependenciesAttribute.java @@ -0,0 +1,38 @@ +/* + * 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.yangjmxgenerator.attribute; + +import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry; +import org.opendaylight.yangtools.binding.generator.util.Types; +import org.opendaylight.yangtools.sal.binding.model.api.Type; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; + +import javax.management.ObjectName; +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import java.util.List; + +public class ListDependenciesAttribute extends AbstractDependencyAttribute { + + public ListDependenciesAttribute(DataSchemaNode attrNode, ServiceInterfaceEntry sie, boolean mandatory, String nullableDescription) { + super(attrNode, sie, mandatory, nullableDescription); + } + + @Override + public Type getType() { + return Types.parameterizedTypeFor(Types.typeForClass(List.class), Types.typeForClass(ObjectName.class)); + } + + @Override + public ArrayType getOpenType() { + OpenType innerOpenType = SimpleType.OBJECTNAME; + return ListAttribute.constructArrayType(innerOpenType); + } + +} diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java index 96656338df..6a540b50b2 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java @@ -13,6 +13,8 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper; +import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl; +import org.opendaylight.yangtools.sal.binding.model.api.Type; import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; @@ -29,11 +31,12 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -public class TOAttribute extends AbstractAttribute { +public class TOAttribute extends AbstractAttribute implements TypedAttribute { private final String nullableDescription, nullableDefault; private final Map yangNameToAttributeMap; private final Map attributeNameMap; + private final String packageName; private static final Set> ALLOWED_CHILDREN = Sets .newHashSet(); @@ -45,7 +48,7 @@ public class TOAttribute extends AbstractAttribute { } public static TOAttribute create( - T containerSchemaNode, TypeProviderWrapper typeProviderWrapper) { + T containerSchemaNode, TypeProviderWrapper typeProviderWrapper, String packageName) { // Transfer Object: get the leaves Map map = new HashMap<>(); Map attributeNameMap = new HashMap<>(); @@ -55,18 +58,18 @@ public class TOAttribute extends AbstractAttribute { String yangName = dataSchemaNode.getQName().getLocalName(); map.put(yangName, createInnerAttribute(dataSchemaNode, - typeProviderWrapper)); + typeProviderWrapper, packageName)); } catch (IllegalArgumentException e) { throw new IllegalStateException("Unable to create TO", e); } } return new TOAttribute(containerSchemaNode, map, attributeNameMap, - containerSchemaNode.getDescription()); + containerSchemaNode.getDescription(), packageName); } private static AttributeIfc createInnerAttribute( DataSchemaNode dataSchemaNode, - TypeProviderWrapper typeProviderWrapper) { + TypeProviderWrapper typeProviderWrapper, String packageName) { Class type = isAllowedType(dataSchemaNode); if (type.equals(LeafSchemaNode.class)) @@ -74,13 +77,13 @@ public class TOAttribute extends AbstractAttribute { typeProviderWrapper); else if (type.equals(ListSchemaNode.class)) return ListAttribute.create((ListSchemaNode) dataSchemaNode, - typeProviderWrapper); + typeProviderWrapper, packageName); else if (type.equals(LeafListSchemaNode.class)) return ListAttribute.create((LeafListSchemaNode) dataSchemaNode, typeProviderWrapper); else if (type.equals(ContainerSchemaNode.class)) return TOAttribute.create((ContainerSchemaNode) dataSchemaNode, - typeProviderWrapper); + typeProviderWrapper, packageName); throw new IllegalStateException("This should never happen"); } @@ -98,12 +101,13 @@ public class TOAttribute extends AbstractAttribute { private TOAttribute(DataSchemaNode attrNode, Map transferObject, - Map attributeNameMap, String nullableDescription) { + Map attributeNameMap, String nullableDescription, String packageName) { super(attrNode); yangNameToAttributeMap = transferObject; this.attributeNameMap = attributeNameMap; this.nullableDescription = nullableDescription; nullableDefault = null; + this.packageName = packageName; } public Map getAttributeNameMap() { @@ -197,6 +201,12 @@ public class TOAttribute extends AbstractAttribute { + yangNameToAttributeMap + '}'; } + @Override + public Type getType() { + // TODO: ReferencedTypeImpl from Types + return new ReferencedTypeImpl(packageName, getUpperCaseCammelCase()); + } + @Override public CompositeType getOpenType() { String description = getNullableDescription() == null ? getAttributeYangName() @@ -222,6 +232,10 @@ public class TOAttribute extends AbstractAttribute { } } + public String getPackageName() { + return packageName; + } + private static final class FunctionImpl implements Function, OpenType> { private final String[] itemNames; diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryNameConflictTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryNameConflictTest.java index deef08a292..9032a2c930 100644 --- a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryNameConflictTest.java +++ b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryNameConflictTest.java @@ -100,6 +100,9 @@ public class ModuleMXBeanEntryNameConflictTest extends AbstractYangTest { testedYangModulesToExpectedConflictingName.put( "config-test-runtime-bean-name-conflict2", "StateARuntimeMXBean"); + testedYangModulesToExpectedConflictingName.put( + "config-test-duplicate-attribute-in-runtime-and-mxbean", + "port"); } private String getYangModuleName(String name) { diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java index 661dbd7da3..e86c876169 100644 --- a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java +++ b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java @@ -7,38 +7,14 @@ */ package org.opendaylight.controller.config.yangjmxgenerator; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -import java.net.URI; -import java.net.URISyntaxException; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; - -import javax.management.openmbean.ArrayType; -import javax.management.openmbean.CompositeType; -import javax.management.openmbean.SimpleType; - +import com.google.common.collect.Sets; import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute; import org.opendaylight.yangtools.binding.generator.util.Types; @@ -48,13 +24,37 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath; -import com.google.common.collect.Sets; +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.SimpleType; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; public class ModuleMXBeanEntryTest extends AbstractYangTest { public static final String EVENTBUS_MXB_NAME = "eventbus"; public static final String ASYNC_EVENTBUS_MXB_NAME = "async-eventbus"; public static final String THREADFACTORY_NAMING_MXB_NAME = "threadfactory-naming"; public static final String THREADPOOL_DYNAMIC_MXB_NAME = "threadpool-dynamic"; + public static final String THREADPOOL_REGISTRY_IMPL_NAME = "threadpool-registry-impl"; public static final String BGP_LISTENER_IMPL_MXB_NAME = "bgp-listener-impl"; @@ -87,7 +87,7 @@ public class ModuleMXBeanEntryTest extends AbstractYangTest { assertNotNull(namesToMBEs); Set expectedMXBeanNames = Sets.newHashSet(EVENTBUS_MXB_NAME, ASYNC_EVENTBUS_MXB_NAME, THREADFACTORY_NAMING_MXB_NAME, - THREADPOOL_DYNAMIC_MXB_NAME); + THREADPOOL_DYNAMIC_MXB_NAME, THREADPOOL_REGISTRY_IMPL_NAME); assertThat(namesToMBEs.keySet(), is(expectedMXBeanNames)); return namesToMBEs; } @@ -299,6 +299,15 @@ public class ModuleMXBeanEntryTest extends AbstractYangTest { } } + { // test multiple dependencies + ModuleMXBeanEntry threadPoolRegistry = namesToMBEs.get(THREADPOOL_REGISTRY_IMPL_NAME); + Map attributes = threadPoolRegistry.getAttributes(); + assertEquals(1, attributes.size()); + AttributeIfc threadpoolsAttr = attributes.get("threadpools"); + assertNotNull(threadpoolsAttr); + assertTrue(threadpoolsAttr instanceof ListDependenciesAttribute); + ListDependenciesAttribute threadpools = (ListDependenciesAttribute) threadpoolsAttr; + } } } diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/SchemaContextTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/SchemaContextTest.java index 24c025755b..14ec7e0fdc 100644 --- a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/SchemaContextTest.java +++ b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/SchemaContextTest.java @@ -100,10 +100,15 @@ public class SchemaContextTest extends AbstractYangTest { @Test public void testReadingIdentities_threadsJavaModule() { - Map> expectedIdentitiesToBases = ImmutableMap - .of("eventbus", Optional.of(MODULE_TYPE_Q_NAME), "async-eventbus", Optional.of(MODULE_TYPE_Q_NAME), - "threadfactory-naming", Optional.of(MODULE_TYPE_Q_NAME), "threadpool-dynamic", - Optional.of(MODULE_TYPE_Q_NAME), "thread-rpc-context", Optional.absent()); + Map> expectedIdentitiesToBases = new HashMap(){{ + put(ModuleMXBeanEntryTest.EVENTBUS_MXB_NAME, Optional.of(MODULE_TYPE_Q_NAME)); + put(ModuleMXBeanEntryTest.ASYNC_EVENTBUS_MXB_NAME, Optional.of(MODULE_TYPE_Q_NAME)); + put(ModuleMXBeanEntryTest.THREADFACTORY_NAMING_MXB_NAME, Optional.of(MODULE_TYPE_Q_NAME)); + put(ModuleMXBeanEntryTest.THREADPOOL_DYNAMIC_MXB_NAME, Optional.of(MODULE_TYPE_Q_NAME)); + put("thread-rpc-context", Optional.absent()); + put(ModuleMXBeanEntryTest.THREADPOOL_REGISTRY_IMPL_NAME, Optional.of(MODULE_TYPE_Q_NAME)); + }}; + assertAllIdentitiesAreExpected(threadsJavaModule, expectedIdentitiesToBases); } diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute-in-runtime-and-mxbean.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute-in-runtime-and-mxbean.yang new file mode 100644 index 0000000000..58c3af262c --- /dev/null +++ b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute-in-runtime-and-mxbean.yang @@ -0,0 +1,57 @@ +// vi: set smarttab et sw=4 tabstop=4: +module config-test-duplicate-attribute-in-runtime-and-mxbean { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:jmx:duplicate:runtime"; + prefix "th-java"; + + import config { prefix config; revision-date 2013-04-05; } + import rpc-context { prefix rpcx; revision-date 2013-06-17; } + import ietf-inet-types { prefix inet; revision-date 2010-09-24;} + + + description + "This module contains the base YANG definitions for NS-OS + thread services pure Java implementation."; + + revision "2013-04-05" { + description + "Updated to work with new anchors."; + } + + revision "2013-04-03" { + description + "Initial revision."; + } + + identity async-eventbus { + base config:module-type; + config:java-name-prefix AsyncEventBus; + } + + augment "/config:modules/config:module/config:configuration" { + case async-eventbus { + when "/config:modules/config:module/config:type = 'async-eventbus'"; + leaf port { + type string; + } + leaf core-size { + type uint32; + } + leaf simple-int3 { + type uint16; + } + } + } + + augment "/config:modules/config:module/config:state" { + case async-eventbus { + when "/config:modules/config:module/config:type = 'async-eventbus'"; + leaf simple-arg { + type uint32; + } + leaf port { + type inet:port-number; + } + } + } +} \ No newline at end of file diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads-java.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads-java.yang index 2972cec04f..4af5b9569a 100644 --- a/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads-java.yang +++ b/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads-java.yang @@ -241,4 +241,25 @@ module config-threads-java { } } } + + identity threadpool-registry-impl { + base config:module-type; + config:java-name-prefix ThreadPoolRegistryImpl; + } + + augment "/config:modules/config:module/config:configuration" { + case threadpool-registry-impl { + when "/config:modules/config:module/config:type = 'threadpool-registry-impl'"; + + // list of dependencies: + list threadpools { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity th2:threadpool; + } + } + } + } + } } diff --git a/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang b/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang index ba5021ea09..9ad7a44915 100644 --- a/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang +++ b/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang @@ -48,28 +48,40 @@ module config-test-impl { container dto-a { leaf simple-arg { type uint32; + default 1; } leaf port { type inet:port-number; + default 8080; } + leaf ip4 { + type inet:ipv4-address; + default 127.0.0.1; + } + + leaf ip { + type inet:ip-address; + // TODO defaults for union default 0:0:0:0:0:0:0:1; + } } leaf as-number { - mandatory true; type inet:as-number; + default 44; } leaf simpleInt { type uint32; - default 99L; + default 99; } container dto_b { leaf simple-int1 { type uint32; + default 32; } leaf simple-int2 { @@ -101,28 +113,34 @@ module config-test-impl { when "/config:modules/config:module/config:type = 'impl-netconf'"; leaf binaryLeaf { type binary; + default ZGVmYXVsdEJpbg==; } leaf type { type string; + default "default-string"; } leaf extended { type tt:extend-once; + default 1; } leaf extended-twice { type tt:extend-twice; + default 2; } leaf extended-enum { type tt:extend-enum; + default ONE; } leaf sleep-factor { type decimal64 { fraction-digits 2; } + default 2.00; } container dto-c { @@ -153,23 +171,28 @@ module config-test-impl { } leaf simple-long { - type int64 ; + type int64; + default -45; } leaf simple-long-2 { type uint32; + default 445; } leaf simple-BigInteger { type uint64; + default 545454; } leaf simple-byte { type int8; + default -4; } leaf simple-short { type uint8; + default 45; } leaf simple-test { @@ -209,6 +232,7 @@ module config-test-impl { container deep { leaf simple-int3 { type uint16; + default 0; } } } @@ -242,6 +266,15 @@ module config-test-impl { } } } + + list testing-deps { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity test:testing; + } + } + } } } @@ -384,6 +417,7 @@ module config-test-impl { container retValContainer { leaf v1 { type string; + default "from rpc"; } leaf v2 { diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index adc0c0973f..4c0b81f7d7 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -441,6 +441,23 @@ org.opendaylight.controller.thirdparty ganymed + + org.opendaylight.controller + sal-remoterpc-connector + 1.0-SNAPSHOT + + + org.opendaylight.controller + + zeromq-routingtable.implementation + + 0.4.1-SNAPSHOT + + + org.zeromq + jeromq + 0.3.1 + diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/00-netty.conf b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/00-netty.conf index e83fdcc5c8..c2f9bc311d 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/00-netty.conf +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/00-netty.conf @@ -20,25 +20,25 @@ netty:netty-threadgroup global-boss-group - /config/modules/module[name='netty-threadgroup-fixed']/instance[name='global-boss-group'] + /modules/module[type='netty-threadgroup-fixed'][name='global-boss-group'] global-worker-group - /config/modules/module[name='netty-threadgroup-fixed']/instance[name='global-worker-group'] + /modules/module[type='netty-threadgroup-fixed'][name='global-worker-group'] netty:netty-event-executor global-event-executor - /config/modules/module[name='netty-global-event-executor']/instance[name='global-event-executor'] + /modules/module[type='netty-global-event-executor'][name='global-event-executor'] netty:netty-timer global-timer - /config/modules/module[name='netty-hashed-wheel-timer']/instance[name='global-timer'] + /modules/module[type='netty-hashed-wheel-timer'][name='global-timer'] //CAPABILITIES START diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.conf b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.conf index 430a278a60..fa33215ea6 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.conf +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.conf @@ -58,60 +58,60 @@ dom:schema-service ref_yang-schema-service - /config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service'] + /modules/module[type='schema-service-singleton'][name='yang-schema-service'] binding:binding-notification-service ref_binding-notification-broker - /config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker'] + /modules/module[type='binding-notification-broker'][name='binding-notification-broker'] dom:dom-data-store ref_hash-map-data-store - /config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store'] + /modules/module[type='hash-map-data-store'][name='hash-map-data-store'] - ref_cluster-data-store - /config/modules/module[name='dom-clustered-store-impl']/instance[name='cluster-data-store'] - + ref_cluster-data-store + /modules/module[type='dom-clustered-store-impl'][name='cluster-data-store'] + binding:binding-broker-osgi-registry ref_binding-broker-impl - /config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl'] + /modules/module[type='binding-broker-impl'][name='binding-broker-impl'] binding:binding-rpc-registry - ref_binding-rpc-broker - /config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl'] + binding-rpc-broker + /modules/module[type='binding-broker-impl'][name='binding-broker-impl'] binding-impl:binding-dom-mapping-service ref_runtime-mapping-singleton - /config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton'] + /modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton'] dom:dom-broker-osgi-registry ref_dom-broker - /config/modules/module[name='dom-broker-impl']/instance[name='dom-broker'] + /modules/module[type='dom-broker-impl'][name='dom-broker'] binding:binding-data-broker ref_binding-data-broker - /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker'] + /modules/module[type='binding-data-broker'][name='binding-data-broker'] //CAPABILITIES START diff --git a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java index de7597730f..3ec9f8443a 100644 --- a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java +++ b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java @@ -836,21 +836,14 @@ public class FlowConfig implements Serializable { sstr = Pattern.compile("OUTPUT=(.*)").matcher(actiongrp); if (sstr.matches()) { for (String t : sstr.group(1).split(",")) { - Matcher n = Pattern.compile("(?:(\\d+))").matcher(t); - if (n.matches()) { - String port = n.group(1); - if (port != null) { - if (!isPortValid(sw, port)) { - String msg = String.format("Output port %s is not valid for this switch", port); - if (!containerName.equals(GlobalConstants.DEFAULT.toString())) { - msg += " in Container " + containerName; - } - return new Status(StatusCode.BADREQUEST, msg); + if (t != null) { + if (!isPortValid(sw, t)) { + String msg = String.format("Output port %s is not valid for this switch", t); + if (!containerName.equals(GlobalConstants.DEFAULT.toString())) { + msg += " in Container " + containerName; } + return new Status(StatusCode.BADREQUEST, msg); } - } else { - String msg = String.format("Output port %s is not valid", t); - return new Status(StatusCode.BADREQUEST, msg); } } continue; @@ -859,21 +852,15 @@ public class FlowConfig implements Serializable { sstr = Pattern.compile("ENQUEUE=(.*)").matcher(actiongrp); if (sstr.matches()) { for (String t : sstr.group(1).split(",")) { - Matcher n = Pattern.compile("(?:(\\d+:\\d+))").matcher(t); - if (n.matches()) { - if (n.group(1) != null) { - String port = n.group(1).split(":")[0]; - if (!isPortValid(sw, port)) { - String msg = String.format("Output port %d is not valid for this switch", port); - if (!containerName.equals(GlobalConstants.DEFAULT.toString())) { - msg += " in Container " + containerName; - } - return new Status(StatusCode.BADREQUEST, msg); + if (t != null) { + String port = t.split(":")[0]; + if (!isPortValid(sw, port)) { + String msg = String.format("Output port %d is not valid for this switch", port); + if (!containerName.equals(GlobalConstants.DEFAULT.toString())) { + msg += " in Container " + containerName; } + return new Status(StatusCode.BADREQUEST, msg); } - } else { - String msg = String.format("Enqueue port %s is not valid", t); - return new Status(StatusCode.BADREQUEST, msg); } } continue; @@ -1102,12 +1089,9 @@ public class FlowConfig implements Serializable { sstr = Pattern.compile(ActionType.OUTPUT + "=(.*)").matcher(actiongrp); if (sstr.matches()) { for (String t : sstr.group(1).split(",")) { - Matcher n = Pattern.compile("(?:(\\d+))").matcher(t); - if (n.matches()) { - if (n.group(1) != null) { - String nc = String.format("%s|%s@%s", node.getType(), n.group(1), node.toString()); - actionList.add(new Output(NodeConnector.fromString(nc))); - } + if (t != null) { + String nc = String.format("%s|%s@%s", node.getType(), t, node.toString()); + actionList.add(new Output(NodeConnector.fromString(nc))); } } continue; @@ -1116,17 +1100,14 @@ public class FlowConfig implements Serializable { sstr = Pattern.compile(ActionType.ENQUEUE + "=(.*)").matcher(actiongrp); if (sstr.matches()) { for (String t : sstr.group(1).split(",")) { - Matcher n = Pattern.compile("(?:(\\d+:\\d+))").matcher(t); - if (n.matches()) { - if (n.group(1) != null) { - String parts[] = n.group(1).split(":"); - String nc = String.format("%s|%s@%s", node.getType(), parts[0], node.toString()); - if (parts.length == 1) { - actionList.add(new Enqueue(NodeConnector.fromString(nc))); - } else { - actionList - .add(new Enqueue(NodeConnector.fromString(nc), Integer.parseInt(parts[1]))); - } + if (t != null) { + String parts[] = t.split(":"); + String nc = String.format("%s|%s@%s", node.getType(), parts[0], node.toString()); + if (parts.length == 1) { + actionList.add(new Enqueue(NodeConnector.fromString(nc))); + } else { + actionList + .add(new Enqueue(NodeConnector.fromString(nc), Integer.parseInt(parts[1]))); } } } diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/FRMUtil.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/FRMUtil.java index 24bfc4fdf4..84b3e53e06 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/FRMUtil.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/FRMUtil.java @@ -260,13 +260,13 @@ public class FRMUtil { } } else if (action instanceof SetDlDstAction) { MacAddress address = ((SetDlDstAction) action).getAddress(); - if (address != null && !isL2AddressValid(address.toString())) { + if (address != null && !isL2AddressValid(address.getValue())) { logger.error("SetDlDstAction: Address not valid"); return false; } } else if (action instanceof SetDlSrcAction) { MacAddress address = ((SetDlSrcAction) action).getAddress(); - if (address != null && !isL2AddressValid(address.toString())) { + if (address != null && !isL2AddressValid(address.getValue())) { logger.error("SetDlSrcAction: Address not valid"); return false; } diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/FlowConsumerImpl.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/FlowConsumerImpl.java index 19c366d014..d2f2420a7c 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/FlowConsumerImpl.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/FlowConsumerImpl.java @@ -2,50 +2,37 @@ package org.opendaylight.controller.forwardingrulesmanager.consumer.impl; import java.util.ArrayList; import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Future; - -import org.opendaylight.controller.clustering.services.CacheConfigException; -import org.opendaylight.controller.clustering.services.CacheExistException; -import org.opendaylight.controller.clustering.services.IClusterContainerServices; -import org.opendaylight.controller.clustering.services.IClusterServices; + import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction; import org.opendaylight.controller.md.sal.common.api.data.DataModification; import org.opendaylight.controller.sal.common.util.Rpcs; -import org.opendaylight.controller.sal.core.IContainer; -import org.opendaylight.controller.sal.utils.ServiceHelper; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.Flows; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.Flow; -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.FlowKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification; -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeFlow; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlowBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlowBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.table.config.rev131024.Tables; +import org.opendaylight.yang.gen.v1.urn.opendaylight.table.config.rev131024.tables.Table; +import org.opendaylight.yang.gen.v1.urn.opendaylight.table.config.rev131024.tables.TableBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.table.config.rev131024.tables.TableKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableRef; import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -55,32 +42,13 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class FlowConsumerImpl implements IForwardingRulesManager { +public class FlowConsumerImpl { protected static final Logger logger = LoggerFactory.getLogger(FlowConsumerImpl.class); private final FlowEventListener flowEventListener = new FlowEventListener(); private Registration listener1Reg; private SalFlowService flowService; // private FlowDataListener listener; - private FlowDataCommitHandler commitHandler; - private static ConcurrentHashMap originalSwView; - private static ConcurrentMap installedSwView; - private IClusterContainerServices clusterContainerService = null; - private IContainer container; - private static final String NAMEREGEX = "^[a-zA-Z0-9]+$"; - private static ConcurrentMap staticFlows; - private static ConcurrentMap staticFlowsOrdinal = new ConcurrentHashMap(); - /* - * Inactive flow list. This is for the global instance of FRM It will - * contain all the flow entries which were installed on the global container - * when the first container is created. - */ - private static ConcurrentMap inactiveFlows; - - /* - * /* Per node indexing - */ - private static ConcurrentMap> nodeFlows; - private boolean inContainerMode; // being used by global instance only + private FlowDataCommitHandler commitHandler; public FlowConsumerImpl() { InstanceIdentifier path = InstanceIdentifier.builder(Flows.class).toInstance(); @@ -90,17 +58,7 @@ public class FlowConsumerImpl implements IForwardingRulesManager { logger.error("Consumer SAL Service is down or NULL. FRM may not function as intended"); return; } - - // listener = new FlowDataListener(); - - // if (null == - // FRMConsumerImpl.getDataBrokerService().registerDataChangeListener(path, - // listener)) { - // logger.error("Failed to listen on flow data modifcation events"); - // System.out.println("Consumer SAL Service is down or NULL."); - // return; - // } - + // For switch events listener1Reg = FRMConsumerImpl.getNotificationService().registerNotificationListener(flowEventListener); @@ -110,66 +68,9 @@ public class FlowConsumerImpl implements IForwardingRulesManager { } // addFlowTest(); commitHandler = new FlowDataCommitHandler(); - FRMConsumerImpl.getDataProviderService().registerCommitHandler(path, commitHandler); - clusterContainerService = (IClusterContainerServices) ServiceHelper.getGlobalInstance( - IClusterContainerServices.class, this); - allocateCaches(); - /* - * If we are not the first cluster node to come up, do not initialize - * the static flow entries ordinal - */ - if (staticFlowsOrdinal.size() == 0) { - staticFlowsOrdinal.put(0, Integer.valueOf(0)); - } + FRMConsumerImpl.getDataProviderService().registerCommitHandler(path, commitHandler); } - - private void allocateCaches() { - - if (this.clusterContainerService == null) { - logger.warn("Un-initialized clusterContainerService, can't create cache"); - return; - } - - try { - clusterContainerService.createCache("frm.originalSwView", - EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - clusterContainerService.createCache("frm.installedSwView", - EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - clusterContainerService - .createCache("frm.staticFlows", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - clusterContainerService.createCache("frm.staticFlowsOrdinal", - EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - clusterContainerService.createCache("frm.inactiveFlows", - EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - clusterContainerService.createCache("frm.nodeFlows", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - clusterContainerService.createCache("frm.groupFlows", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - } catch (CacheConfigException cce) { - logger.error("CacheConfigException"); - } catch (CacheExistException cce) { - logger.error("CacheExistException"); - } - } - - private void addFlowTest() { - try { - NodeRef nodeOne = createNodeRef("foo:node:1"); - AddFlowInputBuilder input1 = new AddFlowInputBuilder(); - - input1.setNode(nodeOne); - AddFlowInput firstMsg = input1.build(); - - if (null == flowService) { - logger.error("ConsumerFlowService is NULL"); - } - @SuppressWarnings("unused") - Future> result1 = flowService.addFlow(firstMsg); - - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - + /** * Adds flow to the southbound plugin and our internal database * @@ -179,42 +80,11 @@ public class FlowConsumerImpl implements IForwardingRulesManager { private void addFlow(InstanceIdentifier path, Flow dataObject) { AddFlowInputBuilder input = new AddFlowInputBuilder(); - + input.fieldsFrom(dataObject); input.setNode((dataObject).getNode()); - input.setPriority((dataObject).getPriority()); - input.setMatch((dataObject).getMatch()); - input.setCookie((dataObject).getCookie()); - input.setInstructions((dataObject).getInstructions()); - input.setBufferId(dataObject.getBufferId()); - input.setTableId(dataObject.getTableId()); - input.setOutPort(dataObject.getOutPort()); - input.setOutGroup(dataObject.getOutGroup()); - input.setIdleTimeout(dataObject.getIdleTimeout()); - input.setHardTimeout(dataObject.getHardTimeout()); - input.setFlowName(dataObject.getFlowName()); - input.setFlags(dataObject.getFlags()); - input.setCookieMask(dataObject.getCookieMask()); - input.setContainerName(dataObject.getContainerName()); - input.setBarrier(dataObject.isBarrier()); - input.setInstallHw(dataObject.isInstallHw()); - input.setStrict(dataObject.isStrict()); - - // updating the staticflow cache - /* - * Commented out... as in many other places... use of ClusteringServices - * is breaking things insufficient time to debug Integer ordinal = - * staticFlowsOrdinal.get(0); staticFlowsOrdinal.put(0, ++ordinal); - * staticFlows.put(ordinal, dataObject); - */ - + input.setFlowTable(new FlowTableRef(createTableInstance(dataObject.getId(), dataObject.getNode()))); // We send flow to the sounthbound plugin - flowService.addFlow(input.build()); - - /* - * Commented out as this will also break due to improper use of - * ClusteringServices updateLocalDatabase((NodeFlow) dataObject, true); - */ } /** @@ -224,41 +94,14 @@ public class FlowConsumerImpl implements IForwardingRulesManager { * @param dataObject */ private void removeFlow(InstanceIdentifier path, Flow dataObject) { - + RemoveFlowInputBuilder input = new RemoveFlowInputBuilder(); + input.fieldsFrom(dataObject); input.setNode((dataObject).getNode()); - input.setPriority((dataObject).getPriority()); - input.setMatch((dataObject).getMatch()); - input.setCookie((dataObject).getCookie()); - input.setInstructions((dataObject).getInstructions()); - input.setBufferId(dataObject.getBufferId()); input.setTableId(dataObject.getTableId()); - input.setOutPort(dataObject.getOutPort()); - input.setOutGroup(dataObject.getOutGroup()); - input.setIdleTimeout(dataObject.getIdleTimeout()); - input.setHardTimeout(dataObject.getHardTimeout()); - input.setFlowName(dataObject.getFlowName()); - input.setFlags(dataObject.getFlags()); - input.setCookieMask(dataObject.getCookieMask()); - input.setContainerName(dataObject.getContainerName()); - input.setBarrier(dataObject.isBarrier()); - input.setInstallHw(dataObject.isInstallHw()); - input.setStrict(dataObject.isStrict()); - // updating the staticflow cache - /* - * Commented out due to problems caused by improper use of - * ClusteringServices Integer ordinal = staticFlowsOrdinal.get(0); - * staticFlowsOrdinal.put(0, ++ordinal); staticFlows.put(ordinal, - * dataObject); - */ - + input.setFlowTable(new FlowTableRef(createTableInstance((long)dataObject.getTableId(), (dataObject).getNode()))); // We send flow to the sounthbound plugin flowService.removeFlow(input.build()); - - /* - * Commented out due to problems caused by improper use of - * ClusteringServices updateLocalDatabase((NodeFlow) dataObject, false); - */ } /** @@ -267,32 +110,19 @@ public class FlowConsumerImpl implements IForwardingRulesManager { * @param path * @param dataObject */ - private void updateFlow(InstanceIdentifier path, Flow dataObject) { + private void updateFlow(InstanceIdentifier path, Flow updatedFlow, Flow originalFlow) { UpdateFlowInputBuilder input = new UpdateFlowInputBuilder(); UpdatedFlowBuilder updatedflowbuilder = new UpdatedFlowBuilder(); - updatedflowbuilder.fieldsFrom(dataObject); - input.setNode(dataObject.getNode()); - input.setUpdatedFlow(updatedflowbuilder.build()); - - // updating the staticflow cache - /* - * Commented out due to problems caused by improper use of - * ClusteringServices. Integer ordinal = staticFlowsOrdinal.get(0); - * staticFlowsOrdinal.put(0, ++ordinal); staticFlows.put(ordinal, - * dataObject); - */ - + updatedflowbuilder.fieldsFrom(updatedFlow); + input.setNode(updatedFlow.getNode()); + input.setUpdatedFlow(updatedflowbuilder.build()); + OriginalFlowBuilder ofb = new OriginalFlowBuilder(originalFlow); + input.setOriginalFlow(ofb.build()); // We send flow to the sounthbound plugin flowService.updateFlow(input.build()); - - /* - * Commented out due to problems caused by improper use of - * ClusteringServices. updateLocalDatabase((NodeFlow) dataObject, true); - */ } - - @SuppressWarnings("unchecked") + private void commitToPlugin(internalTransaction transaction) { Set, DataObject>> createdEntries = transaction.getModification() .getCreatedConfigurationData().entrySet(); @@ -320,16 +150,17 @@ public class FlowConsumerImpl implements IForwardingRulesManager { addFlow(entry.getKey(), (Flow) entry.getValue()); } } - for (@SuppressWarnings("unused") - Entry, DataObject> entry : updatedEntries) { + + for (Entry, DataObject> entry : updatedEntries) { if (entry.getValue() instanceof Flow) { logger.debug("Coming update cc in FlowDatacommitHandler"); - Flow flow = (Flow) entry.getValue(); - boolean status = validate(flow); + Flow updatedFlow = (Flow) entry.getValue(); + Flow originalFlow = (Flow) transaction.modification.getOriginalConfigurationData().get(entry.getKey()); + boolean status = validate(updatedFlow); if (!status) { return; } - updateFlow(entry.getKey(), (Flow) entry.getValue()); + updateFlow(entry.getKey(), updatedFlow, originalFlow); } } @@ -339,21 +170,20 @@ public class FlowConsumerImpl implements IForwardingRulesManager { logger.debug("Coming remove cc in FlowDatacommitHandler"); Flow flow = (Flow) removeValue; boolean status = validate(flow); + if (!status) { return; } + removeFlow(instanceId, (Flow) removeValue); - } } - } - private final class FlowDataCommitHandler implements DataCommitHandler, DataObject> { - + private final class FlowDataCommitHandler implements DataCommitHandler, DataObject> { + @SuppressWarnings("unchecked") - @Override - public DataCommitTransaction requestCommit(DataModification, DataObject> modification) { + public DataCommitTransaction, DataObject> requestCommit(DataModification, DataObject> modification) { // We should verify transaction logger.debug("Coming in FlowDatacommitHandler"); internalTransaction transaction = new internalTransaction(modification); @@ -374,54 +204,23 @@ public class FlowConsumerImpl implements IForwardingRulesManager { public internalTransaction(DataModification, DataObject> modification) { this.modification = modification; } - - Map, Flow> additions = new HashMap<>(); - Map, Flow> updates = new HashMap<>(); - Map, Flow> removals = new HashMap<>(); - + /** * We create a plan which flows will be added, which will be updated and * which will be removed based on our internal state. * */ - void prepareUpdate() { + void prepareUpdate() { - Set, DataObject>> puts = modification.getUpdatedConfigurationData().entrySet(); - for (Entry, DataObject> entry : puts) { - } - - // removals = modification.getRemovedConfigurationData(); - Set> removedData = modification.getRemovedConfigurationData(); - for (InstanceIdentifier removal : removedData) { - DataObject value = modification.getOriginalConfigurationData().get(removal); - if (value instanceof Flow) { - removals.put(removal, (Flow) value); - } - } - - } - - private void preparePutEntry(InstanceIdentifier key, Flow flow) { - Flow original = originalSwView.get(key); - if (original != null) { - // It is update for us - updates.put(key, flow); - } else { - // It is addition for us - additions.put(key, flow); - } } - + /** * We are OK to go with execution of plan * */ @Override public RpcResult finish() throws IllegalStateException { - - commitToPlugin(this); - // We return true if internal transaction is successful. - // return Rpcs.getRpcResult(true, null, Collections.emptySet()); + commitToPlugin(this); return Rpcs.getRpcResult(true, null, Collections. emptySet()); } @@ -431,26 +230,47 @@ public class FlowConsumerImpl implements IForwardingRulesManager { * */ @Override - public RpcResult rollback() throws IllegalStateException { - // NOOP - we did not modified any internal state during - // requestCommit phase - // return Rpcs.getRpcResult(true, null, Collections.emptySet()); + public RpcResult rollback() throws IllegalStateException { + rollBackFlows(modification); return Rpcs.getRpcResult(true, null, Collections. emptySet()); - } + } + } - private boolean flowEntryExists(Flow flow) { - // Flow name has to be unique on per table id basis - for (ConcurrentMap.Entry entry : originalSwView.entrySet()) { - if (entry.getValue().getFlowName().equals(flow.getFlowName()) - && entry.getValue().getTableId().equals(flow.getTableId())) { - return true; - } - } - return false; + private void rollBackFlows(DataModification, DataObject> modification) { + Set, DataObject>> createdEntries = modification.getCreatedConfigurationData().entrySet(); + + /* + * This little dance is because updatedEntries contains both created and modified entries + * The reason I created a new HashSet is because the collections we are returned are immutable. + */ + Set, DataObject>> updatedEntries = new HashSet, DataObject>>(); + updatedEntries.addAll(modification.getUpdatedConfigurationData().entrySet()); + updatedEntries.removeAll(createdEntries); + + Set> removeEntriesInstanceIdentifiers = modification.getRemovedConfigurationData(); + for (Entry, DataObject> entry : createdEntries) { + if(entry.getValue() instanceof Flow) { + removeFlow(entry.getKey(),(Flow) entry.getValue()); // because we are rolling back, remove what we would have added. } } + + for (Entry, DataObject> entry : updatedEntries) { + if(entry.getValue() instanceof Flow) { + Flow updatedFlow = (Flow) entry.getValue(); + Flow originalFlow = (Flow) modification.getOriginalConfigurationData().get(entry.getKey()); + updateFlow(entry.getKey(), updatedFlow ,originalFlow);// because we are rolling back, replace the updated with the original + } + } + + for (InstanceIdentifier instanceId : removeEntriesInstanceIdentifiers ) { + DataObject removeValue = (Flow) modification.getOriginalConfigurationData().get(instanceId); + if(removeValue instanceof Flow) { + addFlow(instanceId,(Flow) removeValue);// because we are rolling back, add what we would have removed. + } + } +} final class FlowEventListener implements SalFlowListener { List addedFlows = new ArrayList<>(); @@ -489,58 +309,18 @@ public class FlowConsumerImpl implements IForwardingRulesManager { // TODO Auto-generated method stub } - } - // Commented out DataChangeListene - to be used by Stats - - // final class FlowDataListener implements DataChangeListener { - // private SalFlowService flowService; - // - // public FlowDataListener() { - // - // } - // - // @Override - // public void onDataChanged( - // DataChangeEvent, DataObject> change) { - // System.out.println("Coming in onDataChange.............."); - // @SuppressWarnings("unchecked") - // Collection additions = (Collection) - // change.getCreatedConfigurationData(); - // // we can check for getCreated, getDeleted or getUpdated from DataChange - // Event class - // for (DataObject dataObject : additions) { - // if (dataObject instanceof NodeFlow) { - // NodeRef nodeOne = createNodeRef("foo:node:1"); - // // validating the dataObject here - // AddFlowInputBuilder input = new AddFlowInputBuilder(); - // input.setNode(((NodeFlow) dataObject).getNode()); - // input.setNode(nodeOne); - // // input.setPriority(((NodeFlow) dataObject).getPriority()); - // //input.setMatch(((NodeFlow) dataObject).getMatch()); - // //input.setFlowTable(((NodeFlow) dataObject).getFlowTable()); - // //input.setCookie(((NodeFlow) dataObject).getCookie()); - // //input.setAction(((NodeFlow) dataObject).getAction()); - // - // @SuppressWarnings("unused") - // Future> result = - // flowService.addFlow(input.build()); - // } - // } - // } - // } - public boolean validate(Flow flow) { - String msg = ""; // Specific part of warn/error log boolean result = true; // flow Name validation - if (flow.getFlowName() == null || flow.getFlowName().trim().isEmpty() || !flow.getFlowName().matches(NAMEREGEX)) { + if (!FRMUtil.isNameValid(flow.getFlowName())) { msg = "Invalid Flow name"; result = false; } + // Node Validation if (result == true && flow.getNode() == null) { msg = "Node is null"; @@ -556,20 +336,7 @@ public class FlowConsumerImpl implements IForwardingRulesManager { result = false; } } - - // Presence check - /* - * This is breaking due to some improper use of caches... - * - * if (flowEntryExists(flow)) { String error = - * "Entry with this name on specified table already exists"; - * logger.warn( - * "Entry with this name on specified table already exists: {}" , - * entry); logger.error(error); return; } if - * (originalSwView.containsKey(entry)) { logger.warn( - * "Operation Rejected: A flow with same match and priority exists on the target node" - * ); logger.trace("Aborting to install {}", entry); continue; } - */ + if (!FRMUtil.validateMatch(flow)) { logger.error("Not a valid Match"); result = false; @@ -584,92 +351,15 @@ public class FlowConsumerImpl implements IForwardingRulesManager { } return result; } - - private static void updateLocalDatabase(NodeFlow entry, boolean add) { - - updateSwViewes(entry, add); - - updateNodeFlowsDB(entry, add); - - } - - /* - * Update the node mapped flows database - */ - private static void updateSwViewes(NodeFlow entry, boolean add) { - if (add) { - FlowConsumerImpl.originalSwView.put((FlowKey) entry, (Flow) entry); - installedSwView.put((FlowKey) entry, (Flow) entry); - } else { - originalSwView.remove(entry); - installedSwView.remove(entry); - - } - } - - @Override - public List get() { - - List orderedList = new ArrayList(); - ConcurrentMap flowMap = staticFlows; - int maxKey = staticFlowsOrdinal.get(0).intValue(); - for (int i = 0; i <= maxKey; i++) { - Flow entry = flowMap.get(i); - if (entry != null) { - orderedList.add(entry); - } - } - return orderedList; - } - - @Override - public DataObject getWithName(String name, org.opendaylight.controller.sal.core.Node n) { - if (this instanceof FlowConsumerImpl) { - for (ConcurrentMap.Entry flowEntry : staticFlows.entrySet()) { - Flow flow = flowEntry.getValue(); - if (flow.getNode().equals(n) && flow.getFlowName().equals(name)) { - - return flowEntry.getValue(); - } - } - } - return null; - } - - /* - * Update the node mapped flows database - */ - private static void updateNodeFlowsDB(NodeFlow entry, boolean add) { - Node node = (Node) entry.getNode(); - - List nodeIndeces = nodeFlows.get(node); - if (nodeIndeces == null) { - if (!add) { - return; - } else { - nodeIndeces = new ArrayList(); - } - } - - if (add) { - nodeIndeces.add((Flow) entry); - } else { - nodeIndeces.remove(entry); - } - - // Update cache across cluster - if (nodeIndeces.isEmpty()) { - nodeFlows.remove(node); - } else { - nodeFlows.put(node, nodeIndeces); - } - } - - private static NodeRef createNodeRef(String string) { - NodeKey key = new NodeKey(new NodeId(string)); - InstanceIdentifier path = InstanceIdentifier.builder().node(Nodes.class).node(Node.class, key) - .toInstance(); - - return new NodeRef(path); + + private InstanceIdentifier createTableInstance(Long tableId, NodeRef nodeRef) { + Table table; + InstanceIdentifier tableInstance; + TableBuilder builder = new TableBuilder(); + builder.setId(tableId); + builder.setKey(new TableKey(tableId, nodeRef)); + table = builder.build(); + tableInstance = InstanceIdentifier.builder(Tables.class).child(Table.class, table.getKey()).toInstance(); + return tableInstance; } } \ No newline at end of file diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java index 6fbbd4d6e2..f4064f22ce 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java @@ -132,25 +132,22 @@ public class GroupConsumerImpl { * @param dataObject */ private void updateGroup(InstanceIdentifier path, - Group originalGroupDataObject, Group updatedGroupDataObject) { - - GroupKey groupKey = updatedGroupDataObject.getKey(); - // Node nodeInstanceID = path.firstIdentifierOf("Node"); + Group updatedGroupDataObject, Group originalGroupDataObject) { UpdatedGroupBuilder updateGroupBuilder = null; Status groupOperationStatus = validateGroup(updatedGroupDataObject); - + if (!groupOperationStatus.isSuccess()) { logger.error("Group data object validation failed %s" + updatedGroupDataObject.getGroupName()); return; } - UpdateGroupInputBuilder groupInputBuilder = new UpdateGroupInputBuilder(); + UpdateGroupInputBuilder groupInputBuilder = new UpdateGroupInputBuilder(); + updateGroupBuilder = new UpdatedGroupBuilder(updatedGroupDataObject); + updateGroupBuilder.setGroupId(new GroupId(updatedGroupDataObject.getId())); groupInputBuilder.setNode(updatedGroupDataObject.getNode()); - updateGroupBuilder = new UpdatedGroupBuilder(updatedGroupDataObject); - updateGroupBuilder.setGroupId(new GroupId(updatedGroupDataObject.getId())); groupInputBuilder.setUpdatedGroup(updateGroupBuilder.build()); OriginalGroupBuilder originalGroupBuilder = new OriginalGroupBuilder(originalGroupDataObject); - groupInputBuilder.setOriginalGroup(originalGroupBuilder.build()); + groupInputBuilder.setOriginalGroup(originalGroupBuilder.build()); groupService.updateGroup(groupInputBuilder.build()); return; } @@ -171,10 +168,8 @@ public class GroupConsumerImpl { } AddGroupInputBuilder groupData = new AddGroupInputBuilder(); - groupData.setBuckets(groupAddDataObject.getBuckets()); - groupData.setContainerName(groupAddDataObject.getContainerName()); - groupData.setGroupId(new GroupId(groupAddDataObject.getId())); - groupData.setGroupType(groupAddDataObject.getGroupType()); + groupData.fieldsFrom(groupAddDataObject); + groupData.setGroupId(new GroupId(groupAddDataObject.getId())); groupData.setNode(groupAddDataObject.getNode()); groupService.addGroup(groupData.build()); return; @@ -196,11 +191,9 @@ public class GroupConsumerImpl { } RemoveGroupInputBuilder groupData = new RemoveGroupInputBuilder(); - groupData.setBuckets(groupRemoveDataObject.getBuckets()); - groupData.setContainerName(groupRemoveDataObject.getContainerName()); - groupData.setGroupId(new GroupId(groupRemoveDataObject.getId())); - groupData.setGroupType(groupRemoveDataObject.getGroupType()); - groupData.setNode(groupRemoveDataObject.getNode()); + groupData.fieldsFrom(groupRemoveDataObject); + groupData.setGroupId(new GroupId(groupRemoveDataObject.getId())); + groupData.setNode(groupRemoveDataObject.getNode()); groupService.removeGroup(groupData.build()); return; } diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/MeterConsumerImpl.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/MeterConsumerImpl.java index 42d0897fb1..bf8c8b7f05 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/MeterConsumerImpl.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/MeterConsumerImpl.java @@ -27,6 +27,13 @@ import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.utils.GlobalConstants; import org.opendaylight.controller.sal.utils.Status; import org.opendaylight.controller.sal.utils.StatusCode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlowBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlowBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.OriginalGroupBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.UpdatedGroupBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.config.rev131024.Meters; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.config.rev131024.meters.Meter; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.config.rev131024.meters.MeterKey; @@ -38,6 +45,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.Rem import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterListener; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.OriginalMeterBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.UpdatedMeterBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.band.type.BandType; @@ -53,35 +61,17 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class MeterConsumerImpl implements IForwardingRulesManager { +public class MeterConsumerImpl { protected static final Logger logger = LoggerFactory.getLogger(MeterConsumerImpl.class); private final MeterEventListener meterEventListener = new MeterEventListener(); private Registration meterListener; private SalMeterService meterService; private MeterDataCommitHandler commitHandler; - private ConcurrentMap originalSwMeterView; - @SuppressWarnings("unused") - private ConcurrentMap installedSwMeterView; - @SuppressWarnings("unused") - private ConcurrentMap> nodeMeters; - @SuppressWarnings("unused") - private ConcurrentMap inactiveMeters; - @SuppressWarnings("unused") - private IContainer container; - private IClusterContainerServices clusterMeterContainerService = null; - public MeterConsumerImpl() { InstanceIdentifier path = InstanceIdentifier.builder(Meters.class).toInstance(); meterService = FRMConsumerImpl.getProviderSession().getRpcService(SalMeterService.class); - clusterMeterContainerService = FRMConsumerImpl.getClusterContainerService(); - container = FRMConsumerImpl.getContainer(); - - if (!(cacheStartup())) { - logger.error("Unable to allocate/retrieve meter cache"); - System.out.println("Unable to allocate/retrieve meter cache"); - } - + if (null == meterService) { logger.error("Consumer SAL Meter Service is down or NULL. FRM may not function as intended"); System.out.println("Consumer SAL Meter Service is down or NULL."); @@ -100,110 +90,7 @@ public class MeterConsumerImpl implements IForwardingRulesManager { commitHandler = new MeterDataCommitHandler(); FRMConsumerImpl.getDataProviderService().registerCommitHandler(path, commitHandler); } - - private boolean allocateMeterCaches() { - if (this.clusterMeterContainerService == null) { - logger.warn("Meter: Un-initialized clusterMeterContainerService, can't create cache"); - return false; - } - - try { - clusterMeterContainerService.createCache("frm.originalSwMeterView", - EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - - clusterMeterContainerService.createCache("frm.installedSwMeterView", - EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - - clusterMeterContainerService.createCache("frm.inactiveMeters", - EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - - clusterMeterContainerService.createCache("frm.nodeMeters", - EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); - - // TODO for cluster mode - /* - * clusterMeterContainerService.createCache(WORK_STATUS_CACHE, - * EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL, - * IClusterServices.cacheMode.ASYNC)); - * - * clusterMeterContainerService.createCache(WORK_ORDER_CACHE, - * EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL, - * IClusterServices.cacheMode.ASYNC)); - */ - - } catch (CacheConfigException cce) { - logger.error("Meter CacheConfigException"); - return false; - - } catch (CacheExistException cce) { - logger.error(" Meter CacheExistException"); - } - - return true; - } - - private void nonClusterMeterObjectCreate() { - originalSwMeterView = new ConcurrentHashMap(); - installedSwMeterView = new ConcurrentHashMap(); - nodeMeters = new ConcurrentHashMap>(); - inactiveMeters = new ConcurrentHashMap(); - } - - @SuppressWarnings({ "unchecked" }) - private boolean retrieveMeterCaches() { - ConcurrentMap map; - - if (this.clusterMeterContainerService == null) { - logger.warn("Meter: un-initialized clusterMeterContainerService, can't retrieve cache"); - nonClusterMeterObjectCreate(); - return false; - } - - map = clusterMeterContainerService.getCache("frm.originalSwMeterView"); - if (map != null) { - originalSwMeterView = (ConcurrentMap) map; - } else { - logger.error("Retrieval of cache(originalSwMeterView) failed"); - return false; - } - - map = clusterMeterContainerService.getCache("frm.installedSwMeterView"); - if (map != null) { - installedSwMeterView = (ConcurrentMap) map; - } else { - logger.error("Retrieval of cache(installedSwMeterView) failed"); - return false; - } - - map = clusterMeterContainerService.getCache("frm.inactiveMeters"); - if (map != null) { - inactiveMeters = (ConcurrentMap) map; - } else { - logger.error("Retrieval of cache(inactiveMeters) failed"); - return false; - } - - map = clusterMeterContainerService.getCache("frm.nodeMeters"); - if (map != null) { - nodeMeters = (ConcurrentMap>) map; - } else { - logger.error("Retrieval of cache(nodeMeter) failed"); - return false; - } - - return true; - } - - private boolean cacheStartup() { - if (allocateMeterCaches()) { - if (retrieveMeterCaches()) { - return true; - } - } - - return false; - } - + /** * Adds Meter to the southbound plugin and our internal database * @@ -213,11 +100,9 @@ public class MeterConsumerImpl implements IForwardingRulesManager { private Status addMeter(InstanceIdentifier path, Meter meterAddDataObject) { MeterKey meterKey = meterAddDataObject.getKey(); - if (null != meterKey && validateMeter(meterAddDataObject, FRMUtil.operation.ADD).isSuccess()) { + if (null != meterKey && validateMeter(meterAddDataObject).isSuccess()) { AddMeterInputBuilder meterBuilder = new AddMeterInputBuilder(); - meterBuilder.setContainerName(meterAddDataObject.getContainerName()); - meterBuilder.setFlags(meterAddDataObject.getFlags()); - meterBuilder.setMeterBandHeaders(meterAddDataObject.getMeterBandHeaders()); + meterBuilder.fieldsFrom(meterAddDataObject); meterBuilder.setMeterId(new MeterId(meterAddDataObject.getId())); meterBuilder.setNode(meterAddDataObject.getNode()); meterService.addMeter(meterBuilder.build()); @@ -235,16 +120,19 @@ public class MeterConsumerImpl implements IForwardingRulesManager { * * @param dataObject */ - private Status updateMeter(InstanceIdentifier path, Meter meterUpdateDataObject) { - MeterKey meterKey = meterUpdateDataObject.getKey(); + private Status updateMeter(InstanceIdentifier path, + Meter updatedMeter, Meter originalMeter) { UpdatedMeterBuilder updateMeterBuilder = null; - if (null != meterKey && validateMeter(meterUpdateDataObject, FRMUtil.operation.UPDATE).isSuccess()) { UpdateMeterInputBuilder updateMeterInputBuilder = new UpdateMeterInputBuilder(); + if (validateMeter(updatedMeter).isSuccess()) { + UpdateMeterInputBuilder updateMeterInputBuilder = new UpdateMeterInputBuilder(); + updateMeterInputBuilder.setNode(updatedMeter.getNode()); updateMeterBuilder = new UpdatedMeterBuilder(); - updateMeterBuilder.fieldsFrom(meterUpdateDataObject); - updateMeterBuilder.setMeterId(new MeterId(meterUpdateDataObject.getId())); - + updateMeterBuilder.fieldsFrom(updatedMeter); + updateMeterBuilder.setMeterId(new MeterId(updatedMeter.getId())); updateMeterInputBuilder.setUpdatedMeter(updateMeterBuilder.build()); + OriginalMeterBuilder originalMeterBuilder = new OriginalMeterBuilder(originalMeter); + updateMeterInputBuilder.setOriginalMeter(originalMeterBuilder.build()); meterService.updateMeter(updateMeterInputBuilder.build()); } else { return new Status(StatusCode.BADREQUEST, "Meter Key or attribute validation failed"); @@ -263,14 +151,11 @@ public class MeterConsumerImpl implements IForwardingRulesManager { private Status removeMeter(InstanceIdentifier path, Meter meterRemoveDataObject) { MeterKey meterKey = meterRemoveDataObject.getKey(); - if (null != meterKey && validateMeter(meterRemoveDataObject, FRMUtil.operation.DELETE).isSuccess()) { + if (null != meterKey && validateMeter(meterRemoveDataObject).isSuccess()) { RemoveMeterInputBuilder meterBuilder = new RemoveMeterInputBuilder(); - meterBuilder.setContainerName(meterRemoveDataObject.getContainerName()); - meterBuilder.setNode(meterRemoveDataObject.getNode()); - meterBuilder.setFlags(meterRemoveDataObject.getFlags()); - meterBuilder.setMeterBandHeaders(meterRemoveDataObject.getMeterBandHeaders()); - meterBuilder.setMeterId(new MeterId(meterRemoveDataObject.getId())); - meterBuilder.setNode(meterRemoveDataObject.getNode()); + meterBuilder.fieldsFrom(meterRemoveDataObject); + meterBuilder.setNode(meterRemoveDataObject.getNode()); + meterBuilder.setMeterId(new MeterId(meterRemoveDataObject.getId())); meterService.removeMeter(meterBuilder.build()); } else { return new Status(StatusCode.BADREQUEST, "Meter Key or attribute validation failed"); @@ -279,22 +164,11 @@ public class MeterConsumerImpl implements IForwardingRulesManager { return new Status(StatusCode.SUCCESS); } - public Status validateMeter(Meter meter, FRMUtil.operation operation) { - String containerName; + public Status validateMeter(Meter meter) { String meterName; Status returnStatus = null; if (null != meter) { - containerName = meter.getContainerName(); - - if (null == containerName) { - containerName = GlobalConstants.DEFAULT.toString(); - } else if (!FRMUtil.isNameValid(containerName)) { - logger.error("Container Name is invalid %s" + containerName); - returnStatus = new Status(StatusCode.BADREQUEST, "Container Name is invalid"); - return returnStatus; - } - meterName = meter.getMeterName(); if (!FRMUtil.isNameValid(meterName)) { logger.error("Meter Name is invalid %s" + meterName); @@ -303,7 +177,7 @@ public class MeterConsumerImpl implements IForwardingRulesManager { } for (int i = 0; i < meter.getMeterBandHeaders().getMeterBandHeader().size(); i++) { - if (!meter.getFlags().isMeterBurst()) { + if (null != meter.getFlags() && !meter.getFlags().isMeterBurst()) { if (0 < meter.getMeterBandHeaders().getMeterBandHeader().get(i).getBurstSize()) { logger.error("Burst size should only be associated when Burst FLAG is set"); returnStatus = new Status(StatusCode.BADREQUEST, @@ -315,7 +189,7 @@ public class MeterConsumerImpl implements IForwardingRulesManager { if (null != returnStatus && !returnStatus.isSuccess()) { return returnStatus; - } else { + } else if (null != meter.getMeterBandHeaders()) { BandType setBandType = null; DscpRemark dscpRemark = null; for (int i = 0; i < meter.getMeterBandHeaders().getMeterBandHeader().size(); i++) { @@ -338,6 +212,47 @@ public class MeterConsumerImpl implements IForwardingRulesManager { return new Status(StatusCode.SUCCESS); } + private RpcResult commitToPlugin(InternalTransaction transaction) { + DataModification, DataObject> modification = transaction.modification; + //get created entries + Set, DataObject>> createdEntries = + modification.getCreatedConfigurationData().entrySet(); + + //get updated entries + Set, DataObject>> updatedEntries = + new HashSet, DataObject>>(); + + updatedEntries.addAll(modification.getUpdatedConfigurationData().entrySet()); + updatedEntries.removeAll(createdEntries); + + //get removed entries + Set> removeEntriesInstanceIdentifiers = + modification.getRemovedConfigurationData(); + + for (Entry, DataObject> entry : createdEntries) { + if(entry.getValue() instanceof Meter) { + addMeter(entry.getKey(), (Meter)entry.getValue()); + } + } + + for (Entry, DataObject> entry : updatedEntries) { + if(entry.getValue() instanceof Meter) { + Meter originalMeter = (Meter) modification.getOriginalConfigurationData().get(entry.getKey()); + Meter updatedMeter = (Meter) entry.getValue(); + updateMeter(entry.getKey(), originalMeter, updatedMeter); + } + } + + for (InstanceIdentifier instanceId : removeEntriesInstanceIdentifiers ) { + DataObject removeValue = modification.getOriginalConfigurationData().get(instanceId); + if(removeValue instanceof Meter) { + removeMeter(instanceId, (Meter)removeValue); + } + } + + return Rpcs.getRpcResult(true, null, Collections.emptySet()); + } + final class InternalTransaction implements DataCommitTransaction, DataObject> { private final DataModification, DataObject> modification; @@ -351,38 +266,13 @@ public class MeterConsumerImpl implements IForwardingRulesManager { this.modification = modification; } - Map, Meter> additions = new HashMap<>(); - Map, Meter> updates = new HashMap<>(); - Set> removals = new HashSet<>(); - /** * We create a plan which flows will be added, which will be updated and * which will be removed based on our internal state. * */ - void prepareUpdate() { - - Set, DataObject>> addMeter = modification.getCreatedConfigurationData().entrySet(); - for (Entry, DataObject> entry : addMeter) { - if (entry.getValue() instanceof Meter) { - Meter meter = (Meter) entry.getValue(); - additions.put(entry.getKey(), meter); - } - - } + void prepareUpdate() { - Set, DataObject>> updateMeter = modification.getUpdatedConfigurationData().entrySet(); - for (Entry, DataObject> entry : updateMeter) { - if (entry.getValue() instanceof Meter) { - Meter meter = (Meter) entry.getValue(); - ///will be fixed once getUpdatedConfigurationData returns only updated data not created data with it. - if (!additions.containsKey(entry.getKey())) { - updates.put(entry.getKey(), meter); - } - } - } - - removals = modification.getRemovedConfigurationData(); } /** @@ -392,9 +282,7 @@ public class MeterConsumerImpl implements IForwardingRulesManager { @Override public RpcResult finish() throws IllegalStateException { - RpcResult rpcStatus = commitToPlugin(this); - // We return true if internal transaction is successful. - // return Rpcs.getRpcResult(true, null, Collections.emptySet()); + RpcResult rpcStatus = commitToPlugin(this); return rpcStatus; } @@ -404,43 +292,13 @@ public class MeterConsumerImpl implements IForwardingRulesManager { * */ @Override - public RpcResult rollback() throws IllegalStateException { - // NOOP - we did not modified any internal state during - // requestCommit phase - // return Rpcs.getRpcResult(true, null, Collections.emptySet()); + public RpcResult rollback() throws IllegalStateException { return Rpcs.getRpcResult(true, null, Collections.emptySet()); } } - private RpcResult commitToPlugin(InternalTransaction transaction) { - for (Entry, Meter> entry : transaction.additions.entrySet()) { - - if (!addMeter(entry.getKey(), entry.getValue()).isSuccess()) { - return Rpcs.getRpcResult(false, null, Collections.emptySet()); - } - } - for (Entry, Meter> entry : transaction.updates.entrySet()) { - - if (!updateMeter(entry.getKey(), entry.getValue()).isSuccess()) { - return Rpcs.getRpcResult(false, null, Collections.emptySet()); - } - } - - for (InstanceIdentifier meterId : transaction.removals) { - DataObject removeValue = transaction.getModification().getOriginalConfigurationData().get(meterId); - - if(removeValue instanceof Meter) { - if(!removeMeter(meterId, (Meter)removeValue).isSuccess()) { - return Rpcs.getRpcResult(false, null, Collections.emptySet()); - } - } - } - - return Rpcs.getRpcResult(true, null, Collections.emptySet()); - } - private final class MeterDataCommitHandler implements DataCommitHandler, DataObject> { @Override public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction, DataObject> requestCommit( @@ -475,32 +333,5 @@ public class MeterConsumerImpl implements IForwardingRulesManager { // TODO Auto-generated method stub } - } - - @Override - public List get() { - - List orderedList = new ArrayList(); - Collection meterList = originalSwMeterView.values(); - for (Iterator iterator = meterList.iterator(); iterator.hasNext();) { - orderedList.add(iterator.next()); - } - return orderedList; - } - - @Override - public DataObject getWithName(String name, Node n) { - if (this instanceof MeterConsumerImpl) { - Collection meterList = originalSwMeterView.values(); - for (Iterator iterator = meterList.iterator(); iterator.hasNext();) { - Meter meter = iterator.next(); - if (meter.getNode().equals(n) && meter.getMeterName().equals(name)) { - - return meter; - } - } - } - - return null; - } + } } diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index 0e78598c11..b34621d02d 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -12,7 +12,7 @@ - + sal-common sal-common-api sal-common-impl @@ -42,45 +42,47 @@ sal-connector-api sal-rest-connector sal-netconf-connector - + + zeromq-routingtable/implementation + sal-remoterpc-connector/implementation clustered-data-store/implementation inventory-manager statistics-manager forwardingrules-manager - + compatibility - zeromq-routingtable/implementation - sal-zeromq-connector - integrationtests - - false - + integrationtests + + false + sal-binding-it - zeromq-routingtable/integrationtest clustered-data-store/integrationtest - test + + + - IDE - - - m2e.version - - - - - target-ide - + IDE + + + m2e.version + + + + + target-ide + @@ -103,9 +105,13 @@ 14.0.1 5.0.0 4.8.1 + 1.5.1 + 1.9.5 2.4.3 2.5 0.5.3.201107060350 + 0.5.1-SNAPSHOT + jacoco reuseReports @@ -119,28 +125,28 @@ - opendaylight-mirror - opendaylight-mirror - ${nexusproxy}/groups/public/ - - false - - - true - never - + opendaylight-mirror + opendaylight-mirror + ${nexusproxy}/groups/public/ + + false + + + true + never + - opendaylight-snapshot - opendaylight-snapshot - ${nexusproxy}/repositories/opendaylight.snapshot/ - - true - - - false - + opendaylight-snapshot + opendaylight-snapshot + ${nexusproxy}/repositories/opendaylight.snapshot/ + + true + + + false + @@ -148,28 +154,28 @@ - opendaylight-mirror - opendaylight-mirror - ${nexusproxy}/groups/public/ - - false - - - true - never - + opendaylight-mirror + opendaylight-mirror + ${nexusproxy}/groups/public/ + + false + + + true + never + - opendaylight-snapshot - opendaylight-snapshot - ${nexusproxy}/repositories/opendaylight.snapshot/ - - true - - - false - + opendaylight-snapshot + opendaylight-snapshot + ${nexusproxy}/repositories/opendaylight.snapshot/ + + true + + + false + @@ -216,6 +222,11 @@ yang-data-api ${yang.version} + + org.opendaylight.yangtools + yang-data-impl + ${yang.version} + org.opendaylight.yangtools yang-model-api @@ -232,6 +243,17 @@ sal-connector-api ${project.version} + + org.opendaylight.controller + sal + ${sal.version} + + + org.osgi + org.osgi.compendium + + + @@ -249,7 +271,11 @@ org.eclipse.xtend.lib ${xtend.version} - + + org.osgi + org.osgi.core + ${osgi.core.version} + junit @@ -260,7 +286,25 @@ org.mockito mockito-all - 1.9.5 + ${mockito.version} + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + + + org.powermock + powermock-core + ${powermock.version} test @@ -278,15 +322,9 @@ maven-bundle-plugin ${bundle.plugin.version} true - + ${project.groupId}.${project.artifactId} @@ -328,7 +366,8 @@ jacoco-maven-plugin ${jacoco.version} - + org.eclipse.m2e lifecycle-mapping @@ -346,7 +385,7 @@ - + @@ -360,7 +399,7 @@ - + @@ -373,7 +412,7 @@ - + diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/statistics/DataBrokerRuntimeMXBeanImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/statistics/DataBrokerRuntimeMXBeanImpl.java index a1a24ebc8a..5da084e9bd 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/statistics/DataBrokerRuntimeMXBeanImpl.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/statistics/DataBrokerRuntimeMXBeanImpl.java @@ -1,14 +1,15 @@ package org.opendaylight.controller.config.yang.md.sal.binding.statistics; +import org.opendaylight.controller.config.yang.md.sal.binding.impl.Data; import org.opendaylight.controller.config.yang.md.sal.binding.impl.DataBrokerImplRuntimeMXBean; import org.opendaylight.controller.config.yang.md.sal.binding.impl.Transactions; import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl; public class DataBrokerRuntimeMXBeanImpl extends DataBrokerImpl implements DataBrokerImplRuntimeMXBean { - private Transactions transactions = new Transactions(); + private final Transactions transactions = new Transactions(); + private final Data data = new Data(); - @Override public Transactions getTransactions() { transactions.setCreated(getCreatedTransactionsCount().get()); transactions.setSubmitted(getSubmittedTransactionsCount().get()); @@ -16,4 +17,14 @@ public class DataBrokerRuntimeMXBeanImpl extends DataBrokerImpl implements DataB transactions.setFailed(getFailedTransactionsCount().get()); return transactions; } + + @Override + public Data getData() { + transactions.setCreated(getCreatedTransactionsCount().get()); + transactions.setSubmitted(getSubmittedTransactionsCount().get()); + transactions.setSuccessful(getFinishedTransactionsCount().get()); + transactions.setFailed(getFailedTransactionsCount().get()); + data.setTransactions(transactions); + return data; + } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/api/CodecRegistry.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/api/CodecRegistry.java index a31d3eedd2..b9a4fe87ac 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/api/CodecRegistry.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/api/CodecRegistry.java @@ -2,6 +2,7 @@ package org.opendaylight.controller.sal.binding.dom.serializer.api; import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.Identifier; @@ -14,6 +15,8 @@ import org.opendaylight.controller.sal.binding.dom.serializer.api.IdentifierCode public interface CodecRegistry { InstanceIdentifierCodec getInstanceIdentifierCodec(); + + IdentitityCodec getIdentityCodec(); DataContainerCodec getCodecForDataObject(Class object); @@ -22,6 +25,8 @@ public interface CodecRegistry { > IdentifierCodec getCodecForIdentifier(Class object); > AugmentationCodec getCodecForAugmentation(Class object); + + IdentitityCodec getCodecForIdentity(Class codec); Class getClassForPath(List names); diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/api/IdentitityCodec.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/api/IdentitityCodec.java new file mode 100644 index 0000000000..0c480f52f1 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/api/IdentitityCodec.java @@ -0,0 +1,14 @@ +package org.opendaylight.controller.sal.binding.dom.serializer.api; + +import org.opendaylight.yangtools.yang.binding.BaseIdentity; +import org.opendaylight.yangtools.yang.binding.BindingCodec; +import org.opendaylight.yangtools.yang.common.QName; + +public interface IdentitityCodec extends BindingCodec>{ + + @Override + public QName serialize(Class input); + + @Override + public Class deserialize(QName input); +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/CodecMapping.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/CodecMapping.java index 172fb05292..fa2d32a138 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/CodecMapping.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/CodecMapping.java @@ -3,6 +3,7 @@ package org.opendaylight.controller.sal.binding.dom.serializer.impl; import java.lang.reflect.Field; import java.util.Map; +import org.opendaylight.controller.sal.binding.dom.serializer.api.IdentitityCodec; import org.opendaylight.controller.sal.binding.dom.serializer.api.InstanceIdentifierCodec; import org.opendaylight.yangtools.yang.binding.BindingCodec; import org.opendaylight.yangtools.yang.common.QName; @@ -15,6 +16,8 @@ public class CodecMapping { private static final Logger LOG = LoggerFactory.getLogger(CodecMapping.class); public static final String INSTANCE_IDENTIFIER_CODEC = "INSTANCE_IDENTIFIER_CODEC"; + public static final String IDENTITYREF_CODEC = "IDENTITYREF_CODEC"; + public static final String CLASS_TO_CASE_MAP = "CLASS_TO_CASE"; public static final String COMPOSITE_TO_CASE = "COMPOSITE_TO_CASE"; public static final String AUGMENTATION_CODEC = "AUGMENTATION_CODEC"; @@ -27,7 +30,22 @@ public class CodecMapping { instanceIdField.set(null, codec); } } catch (NoSuchFieldException e) { - LOG.debug("Instance identifier codec is not needed for {}",obj.getName(),e); + LOG.trace("Instance identifier codec is not needed for {}",obj.getName(),e); + } catch (SecurityException | IllegalAccessException e) { + LOG.error("Instance identifier could not be set for {}",obj.getName(),e); + } + } + + + public static void setIdentityRefCodec(Class obj,IdentitityCodec codec) { + Field instanceIdField; + try { + instanceIdField = obj.getField(IDENTITYREF_CODEC); + if(obj != null) { + instanceIdField.set(null, codec); + } + } catch (NoSuchFieldException e) { + LOG.trace("Instance identifier codec is not needed for {}",obj.getName(),e); } catch (SecurityException | IllegalAccessException e) { LOG.error("Instance identifier could not be set for {}",obj.getName(),e); } @@ -71,4 +89,18 @@ public class CodecMapping { LOG.error("Augmentation codec could not be set for {}",dataCodec.getName(),e); } } + + + public static BindingCodec getAugmentationCodec(Class> dataCodec) { + Field instanceIdField; + try { + instanceIdField = dataCodec.getField(AUGMENTATION_CODEC); + return (BindingCodec) instanceIdField.get(null); + } catch (NoSuchFieldException e) { + LOG.debug("BUG: Augmentation codec is not needed for {}",dataCodec.getName(),e); + } catch (SecurityException | IllegalAccessException e) { + LOG.error("Augmentation codec could not be set for {}",dataCodec.getName(),e); + } + return null; + } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/IntermediateMapping.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/IntermediateMapping.xtend index d0b114e3c3..04b8674727 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/IntermediateMapping.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/IntermediateMapping.xtend @@ -13,7 +13,10 @@ class IntermediateMapping { - static def Node toNode(Map map) { + static def Node toNode(Map map) { + if(map instanceof Node) { + return map as Node; + } val nodeMap = map as Map; Preconditions.checkArgument(map.size == 1); val elem = nodeMap.entrySet.iterator.next; @@ -22,10 +25,15 @@ class IntermediateMapping { toNodeImpl(qname, value); } + static def dispatch Node toNodeImpl(QName name, List objects) { val values = new ArrayList>(objects.size); for (obj : objects) { - values.add(toNode(obj as Map)); + if(obj instanceof Node) { + values.add(obj as Node); + } else if(obj instanceof Map) { + values.add(toNode(obj as Map)); + } } return new CompositeNodeTOImpl(name, null, values); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/LazyGeneratedCodecRegistry.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/LazyGeneratedCodecRegistry.java index d33272d641..39bd0816f5 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/LazyGeneratedCodecRegistry.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/LazyGeneratedCodecRegistry.java @@ -1,7 +1,9 @@ package org.opendaylight.controller.sal.binding.dom.serializer.impl; +import java.awt.CompositeContext; import java.lang.ref.WeakReference; import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -12,11 +14,13 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.Set; import java.util.WeakHashMap; +import org.apache.commons.lang3.text.translate.AggregateTranslator; import org.opendaylight.controller.sal.binding.dom.serializer.api.AugmentationCodec; import org.opendaylight.controller.sal.binding.dom.serializer.api.ChoiceCaseCodec; import org.opendaylight.controller.sal.binding.dom.serializer.api.ChoiceCodec; @@ -24,6 +28,7 @@ import org.opendaylight.controller.sal.binding.dom.serializer.api.CodecRegistry; import org.opendaylight.controller.sal.binding.dom.serializer.api.DataContainerCodec; import org.opendaylight.controller.sal.binding.dom.serializer.api.DomCodec; import org.opendaylight.controller.sal.binding.dom.serializer.api.IdentifierCodec; +import org.opendaylight.controller.sal.binding.dom.serializer.api.IdentitityCodec; import org.opendaylight.controller.sal.binding.dom.serializer.api.InstanceIdentifierCodec; import org.opendaylight.controller.sal.binding.dom.serializer.api.ValueWithQName; import org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils; @@ -34,6 +39,7 @@ import org.opendaylight.yangtools.concepts.Delegator; import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.yang.binding.Augmentable; import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; import org.opendaylight.yangtools.yang.binding.BindingCodec; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -41,6 +47,7 @@ import org.opendaylight.yangtools.yang.binding.Identifier; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; @@ -61,10 +68,12 @@ import static org.opendaylight.controller.sal.binding.dom.serializer.impl.Interm import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleContext; import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType; import org.opendaylight.yangtools.sal.binding.model.api.Type; +import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder; import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; +import com.google.common.collect.FluentIterable; import com.google.common.util.concurrent.CycleDetectingLockFactory.WithExplicitOrdering; public class LazyGeneratedCodecRegistry implements // @@ -76,27 +85,31 @@ public class LazyGeneratedCodecRegistry implements // private final static LateMixinCodec NOT_READY_CODEC = new LateMixinCodec(); private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this); + private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec(); private TransformerGenerator generator; // Concrete class to codecs - private Map, DataContainerCodec> containerCodecs = new WeakHashMap<>(); - private Map, IdentifierCodec> identifierCodecs = new WeakHashMap<>(); - private Map, ChoiceCodecImpl> choiceCodecs = new WeakHashMap<>(); - private Map, ChoiceCaseCodecImpl> caseCodecs = new WeakHashMap<>(); - private Map, AugmentableCompositeCodec> augmentableCodecs = new WeakHashMap<>(); - + private static final Map, DataContainerCodec> containerCodecs = new WeakHashMap<>(); + private static final Map, IdentifierCodec> identifierCodecs = new WeakHashMap<>(); + private static final Map, ChoiceCodecImpl> choiceCodecs = new WeakHashMap<>(); + private static final Map, ChoiceCaseCodecImpl> caseCodecs = new WeakHashMap<>(); + private static final Map, AugmentableCompositeCodec> augmentableCodecs = new WeakHashMap<>(); + private static final Map, AugmentationCodec> augmentationCodecs = new WeakHashMap<>(); + private static final Map, QName> identityQNames = new WeakHashMap<>(); + private static final Map qnamesToIdentityMap = new ConcurrentHashMap<>(); /** Binding type to encountered classes mapping **/ @SuppressWarnings("rawtypes") - Map> typeToClass = new ConcurrentHashMap<>(); + private static final Map> typeToClass = new ConcurrentHashMap<>(); @SuppressWarnings("rawtypes") - private ConcurrentMap typeToCaseCodecs = new ConcurrentHashMap<>(); + private static final ConcurrentMap typeToCaseCodecs = new ConcurrentHashMap<>(); private CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade(); - Map pathToType = new ConcurrentHashMap<>(); - Map, Type> pathToInstantiatedType = new ConcurrentHashMap<>(); + private static final Map pathToType = new ConcurrentHashMap<>(); + private static final Map, Type> pathToInstantiatedType = new ConcurrentHashMap<>(); + private static final Map typeToQname = new ConcurrentHashMap<>(); private SchemaContext currentSchema; @@ -115,8 +128,56 @@ public class LazyGeneratedCodecRegistry implements // @Override public > AugmentationCodec getCodecForAugmentation(Class object) { - // TODO Auto-generated method stub - return null; + AugmentationCodec codec = null; + @SuppressWarnings("rawtypes") + AugmentationCodec potentialCodec = augmentationCodecs.get(object); + if (potentialCodec != null) { + codec = potentialCodec; + } else + try { + Class, Object>> augmentRawCodec = generator + .augmentationTransformerFor(object); + BindingCodec, Object> rawCodec = augmentRawCodec.newInstance(); + codec = new AugmentationCodecWrapper(rawCodec); + augmentationCodecs.put(augmentRawCodec, codec); + } catch (InstantiationException e) { + LOG.error("Can not instantiate raw augmentation codec {}", object.getSimpleName(), e); + } catch (IllegalAccessException e) { + LOG.debug("BUG: Constructor for {} is not accessible.", object.getSimpleName(), e); + } + Class> objectSupertype = getAugmentableArgumentFrom(object); + if (objectSupertype != null) { + getAugmentableCodec(objectSupertype).addAugmentationCodec(object, codec); + } else { + LOG.warn("Could not find augmentation target for augmentation {}", object); + } + return codec; + } + + private static Class> getAugmentableArgumentFrom( + final Class> augmentation) { + try { + Class> ret = ClassLoaderUtils.withClassLoader(augmentation.getClassLoader(), + new Callable>>() { + @Override + @SuppressWarnings("unchecked") + public Class> call() throws Exception { + for (java.lang.reflect.Type supertype : augmentation.getGenericInterfaces()) { + if (supertype instanceof ParameterizedType + && Augmentation.class.equals(((ParameterizedType) supertype).getRawType())) { + ParameterizedType augmentationGeneric = (ParameterizedType) supertype; + return (Class>) augmentationGeneric + .getActualTypeArguments()[0]; + } + } + return null; + } + }); + return ret; + } catch (Exception e) { + LOG.error("Could not find augmentable for {}", augmentation, e); + return null; + } } @Override @@ -165,7 +226,6 @@ public class LazyGeneratedCodecRegistry implements // return newWrapper; } - @Override @SuppressWarnings("rawtypes") public void bindingClassEncountered(Class cls) { @@ -268,9 +328,21 @@ public class LazyGeneratedCodecRegistry implements // return newWrapper; } + @Override + public IdentitityCodec getIdentityCodec() { + return identityRefCodec; + } + + @Override + public IdentitityCodec getCodecForIdentity(Class codec) { + bindingClassEncountered(codec); + return identityRefCodec; + } + @Override public void onCodecCreated(Class cls) { CodecMapping.setIdentifierCodec(cls, instanceIdentifierCodec); + CodecMapping.setIdentityRefCodec(cls, identityRefCodec); } @Override @@ -316,6 +388,10 @@ public class LazyGeneratedCodecRegistry implements // public void onModuleContextAdded(SchemaContext schemaContext, Module module, ModuleContext context) { pathToType.putAll(context.getChildNodes()); + qnamesToIdentityMap.putAll(context.getIdentities()); + for(Entry identity : context.getIdentities().entrySet()) { + typeToQname.put(new ReferencedTypeImpl(identity.getValue().getPackageName(), identity.getValue().getName()),identity.getKey()); + } captureCases(context.getCases(), schemaContext); } @@ -324,7 +400,6 @@ public class LazyGeneratedCodecRegistry implements // ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode .getValue().getName()); - LOG.info("Case path: {} Type : {}", caseNode.getKey(), caseNode.getValue().getFullyQualifiedName()); pathToType.put(caseNode.getKey(), caseNode.getValue()); ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey()); @@ -371,10 +446,10 @@ public class LazyGeneratedCodecRegistry implements // if (path != null && (type = pathToType.get(path)) != null) { ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName()); ChoiceCaseCodecImpl partialCodec = typeToCaseCodecs.get(typeref); - if(partialCodec.getSchema() == null ) { + if (partialCodec.getSchema() == null) { partialCodec.setSchema(caseNode); } - + Class caseClass = ClassLoaderUtils.tryToLoadClassWithTCCL(type.getFullyQualifiedName()); if (caseClass != null) { getCaseCodecFor(caseClass); @@ -402,7 +477,7 @@ public class LazyGeneratedCodecRegistry implements // } - private AugmentableCompositeCodec getAugmentableCodec(Class dataClass) { + public AugmentableCompositeCodec getAugmentableCodec(Class dataClass) { AugmentableCompositeCodec ret = augmentableCodecs.get(dataClass); if (ret != null) { return ret; @@ -739,7 +814,7 @@ public class LazyGeneratedCodecRegistry implements // private final Class augmentableType; - Map rawAugmentationCodecs = new WeakHashMap<>(); + Map> localAugmentationCodecs = new WeakHashMap<>(); public AugmentableCompositeCodec(Class type) { checkArgument(Augmentable.class.isAssignableFrom(type)); @@ -772,53 +847,33 @@ public class LazyGeneratedCodecRegistry implements // private List serializeImpl(Map input) { List ret = new ArrayList<>(); for (Entry entry : input.entrySet()) { - BindingCodec codec = getRawCodecForAugmentation(entry.getKey()); - List output = (List) codec.serialize(new ValueWithQName(null, entry.getValue())); - ret.addAll(output); + AugmentationCodec codec = getCodecForAugmentation(entry.getKey()); + CompositeNode node = codec.serialize(new ValueWithQName(null, entry.getValue())); + ret.addAll(node.getChildren()); } return ret; } - private BindingCodec getRawCodecForAugmentation(Class key) { - BindingCodec ret = rawAugmentationCodecs.get(key); - if (ret != null) { - return ret; - } - try { - Class retClass = generator.augmentationTransformerFor(key); - ret = retClass.newInstance(); - rawAugmentationCodecs.put(key, ret); - return ret; - } catch (InstantiationException e) { - LOG.error("Can not instantiate raw augmentation codec {}", key.getSimpleName(), e); - } catch (IllegalAccessException e) { - LOG.debug("BUG: Constructor for {} is not accessible.", key.getSimpleName(), e); - } - return null; + public synchronized > void addAugmentationCodec(Class augmentationClass, + AugmentationCodec value) { + localAugmentationCodecs.put(augmentationClass, value); } @Override public Map deserialize(Object input) { Map ret = new HashMap<>(); if (input instanceof CompositeNode) { - for (Entry codec : rawAugmentationCodecs.entrySet()) { - Augmentation value = (Augmentation) codec.getValue().deserialize(input); - if (value != null) { - ret.put(codec.getKey(), value); + List>> codecs = new ArrayList<>(localAugmentationCodecs.entrySet()); + for (Entry> codec : codecs) { + ValueWithQName value = codec.getValue().deserialize((CompositeNode) input); + if (value != null && value.getValue() != null) { + ret.put(codec.getKey(), (Augmentation) value.getValue()); } } } return ret; } - public Map getRawAugmentationCodecs() { - return rawAugmentationCodecs; - } - - public void setRawAugmentationCodecs(Map rawAugmentationCodecs) { - this.rawAugmentationCodecs = rawAugmentationCodecs; - } - public Class getAugmentableType() { return augmentableType; } @@ -847,4 +902,82 @@ public class LazyGeneratedCodecRegistry implements // return getDelegate().serialize(input); } } + + private static class AugmentationCodecWrapper> implements AugmentationCodec, + Delegator { + + private BindingCodec delegate; + + public AugmentationCodecWrapper(BindingCodec, Object> rawCodec) { + this.delegate = rawCodec; + } + + @Override + public BindingCodec getDelegate() { + return delegate; + } + + @Override + public CompositeNode serialize(ValueWithQName input) { + @SuppressWarnings("unchecked") + List> rawValues = (List>) getDelegate().serialize(input); + List> serialized = new ArrayList<>(rawValues.size()); + for (Map val : rawValues) { + serialized.add(toNode(val)); + } + return new CompositeNodeTOImpl(input.getQname(), null, serialized); + } + + @Override + @SuppressWarnings("unchecked") + public ValueWithQName deserialize(Node input) { + Object rawCodecValue = getDelegate().deserialize((Map) input); + return new ValueWithQName(input.getNodeType(), (T) rawCodecValue); + } + } + + private class IdentityCompositeCodec implements IdentitityCodec { + + @Override + public Object deserialize(Object input) { + checkArgument(input instanceof QName); + return deserialize((QName) input); + } + + @Override + public Class deserialize(QName input) { + Type type = qnamesToIdentityMap.get(input); + if(type == null) { + return null; + } + ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName()); + WeakReference softref = typeToClass.get(typeref); + if(softref == null) { + return null; + } + return softref.get(); + } + + @Override + public QName serialize(Class input) { + checkArgument(BaseIdentity.class.isAssignableFrom(input)); + bindingClassEncountered(input); + QName qname = identityQNames.get(input); + if(qname != null) { + return qname; + } + ConcreteType typeref = Types.typeForClass(input); + qname = typeToQname.get(typeref); + if(qname != null) { + identityQNames.put(input, qname); + } + return qname; + } + + @Override + public Object serialize(Object input) { + checkArgument(input instanceof Class); + return serialize((Class) input); + } + } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/RuntimeGeneratedMappingServiceImpl.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/RuntimeGeneratedMappingServiceImpl.xtend index cb25f4da8b..13975cad4c 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/RuntimeGeneratedMappingServiceImpl.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/RuntimeGeneratedMappingServiceImpl.xtend @@ -37,6 +37,12 @@ import org.opendaylight.controller.sal.binding.impl.connect.dom.DeserializationE import java.util.concurrent.Callable import org.opendaylight.yangtools.yang.binding.Augmentation import org.opendaylight.controller.sal.binding.impl.util.YangSchemaUtils +import org.opendaylight.controller.sal.binding.dom.serializer.api.AugmentationCodec +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates +import java.util.ArrayList +import org.opendaylight.yangtools.yang.data.api.Node +import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl +import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaServiceListener, AutoCloseable { @@ -112,9 +118,21 @@ class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingSer override Entry toDataDom( Entry, DataObject> entry) { + + try { val key = toDataDom(entry.key) - val data = toCompositeNodeImpl(entry.value); + var CompositeNode data; + if(Augmentation.isAssignableFrom(entry.key.targetType)) { + data = toCompositeNodeImpl(key,entry.value); + } else { + data = toCompositeNodeImpl(entry.value); + } return new SimpleEntry(key, data); + + } catch (Exception e) { + LOG.error("Error during serialization for {}.", entry.key,e); + throw e; + } } private def CompositeNode toCompositeNodeImpl(DataObject object) { @@ -124,6 +142,26 @@ class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingSer val ret = codec.serialize(new ValueWithQName(null, object)); return ret as CompositeNode; } + + + private def CompositeNode toCompositeNodeImpl(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier,DataObject object) { + + //val cls = object.implementedInterface; + //waitForSchema(cls); + val last = identifier.path.last; + val codec = registry.getCodecForAugmentation(object.implementedInterface as Class) as AugmentationCodec; + val ret = codec.serialize(new ValueWithQName(last.nodeType, object)); + if(last instanceof NodeIdentifierWithPredicates) { + val predicates = last as NodeIdentifierWithPredicates; + val newNodes = new ArrayList>(predicates.keyValues.size); + for(predicate : predicates.keyValues.entrySet) { + newNodes.add(new SimpleNodeTOImpl(predicate.key,null,predicate.value)); + } + newNodes.addAll(ret.children); + return new CompositeNodeTOImpl(last.nodeType,null,newNodes); + } + return ret as CompositeNode; + } private def void waitForSchema(Class class1) { if(Augmentation.isAssignableFrom(class1)) { diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/TransformerGenerator.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/TransformerGenerator.xtend index 0316614aa1..b2d25af885 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/TransformerGenerator.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/TransformerGenerator.xtend @@ -91,6 +91,8 @@ class TransformerGenerator { @Property var GeneratorListener listener; + + public static val CLASS_TYPE = Types.typeForClass(Class); public new(ClassPool pool) { classPool = pool; @@ -269,6 +271,7 @@ class TransformerGenerator { val ctCls = createClass(inputType.codecClassName) [ //staticField(Map,"AUGMENTATION_SERIALIZERS"); staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec) + staticField(it, IDENTITYREF_CODEC, BindingCodec) staticQNameField(node.QName); implementsType(BINDING_CODEC) method(Object, "toDomStatic", QName, Object) [ @@ -351,6 +354,7 @@ class TransformerGenerator { staticQNameField(node.QName); staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec) staticField(it, AUGMENTATION_CODEC, BindingCodec) + staticField(it, IDENTITYREF_CODEC, BindingCodec) method(Object, "toDomStatic", QName, Object) [ modifiers = PUBLIC + FINAL + STATIC body = ''' @@ -409,6 +413,7 @@ class TransformerGenerator { //staticField(Map,"AUGMENTATION_SERIALIZERS"); staticQNameField(node.QName); staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec) + staticField(it, IDENTITYREF_CODEC, BindingCodec) staticField(it, AUGMENTATION_CODEC, BindingCodec) implementsType(BINDING_CODEC) method(Object, "toDomStatic", QName, Object) [ @@ -459,6 +464,7 @@ class TransformerGenerator { staticQNameField(node.augmentationQName); staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec) staticField(it, AUGMENTATION_CODEC, BindingCodec) + staticField(it, IDENTITYREF_CODEC, BindingCodec) implementsType(BINDING_CODEC) method(Object, "toDomStatic", QName, Object) [ modifiers = PUBLIC + FINAL + STATIC @@ -466,7 +472,7 @@ class TransformerGenerator { { //System.out.println("Qname " + $1); //System.out.println("Value " + $2); - «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName()); + «QName.name» _resultName = «QName.name».create(QNAME,QNAME.getLocalName()); java.util.List _childNodes = new java.util.ArrayList(); «type.resolvedName» value = («type.resolvedName») $2; «FOR child : node.childNodes» @@ -540,6 +546,7 @@ class TransformerGenerator { //staticField(Map,"AUGMENTATION_SERIALIZERS"); //staticQNameField(inputType); staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec) + staticField(it, IDENTITYREF_CODEC, BindingCodec) staticField(it, CLASS_TO_CASE_MAP, Map) staticField(it, COMPOSITE_TO_CASE, Map) //staticField(it,QNAME_TO_CASE_MAP,BindingCodec) @@ -828,6 +835,7 @@ class TransformerGenerator { if (hasYangBinding) { implementsType(BINDING_CODEC) staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec) + staticField(it, IDENTITYREF_CODEC, BindingCodec) implementsType(BindingDeserializer.asCtClass) } method(Object, "toDomValue", Object) [ @@ -1020,10 +1028,10 @@ class TransformerGenerator { private def dispatch String deserializeValue(Type type, String domParameter) { if (INSTANCE_IDENTIFIER.equals(type)) { - return '''(«InstanceIdentifier.name») «INSTANCE_IDENTIFIER_CODEC».deserialize(«domParameter»)''' + } else if (CLASS_TYPE.equals(type)) { + return '''(«Class.name») «IDENTITYREF_CODEC».deserialize(«domParameter»)''' } - return '''(«type.resolvedName») «domParameter»''' } @@ -1192,6 +1200,8 @@ class TransformerGenerator { private def dispatch serializeValue(Type signature, String property) { if (INSTANCE_IDENTIFIER == signature) { return '''«INSTANCE_IDENTIFIER_CODEC».serialize(«property»)''' + }else if (CLASS_TYPE.equals(signature)) { + return '''(«QName.resolvedName») «IDENTITYREF_CODEC».serialize(«property»)''' } return '''«property»'''; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentDataServiceConnector.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentDataServiceConnector.java index 5f3189f7d2..9eff29f8cc 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentDataServiceConnector.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentDataServiceConnector.java @@ -22,6 +22,8 @@ import org.opendaylight.controller.sal.core.api.Provider; import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction; import org.opendaylight.yangtools.concepts.Registration; +import org.opendaylight.yangtools.yang.binding.Augmentable; +import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcError; @@ -61,8 +63,22 @@ public class BindingIndependentDataServiceConnector implements // public DataObject readOperationalData(InstanceIdentifier path) { try { org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path); + + CompositeNode result = biDataService.readOperationalData(biPath); + Class targetType = path.getTargetType(); + + if(Augmentation.class.isAssignableFrom(targetType)) { + path = mappingService.fromDataDom(biPath); + Class> augmentType = (Class>) targetType; + DataObject parentTo = mappingService.dataObjectFromDataDom(path, result); + if(parentTo instanceof Augmentable) { + return (DataObject) ((Augmentable) parentTo).getAugmentation(augmentType); + } + + } return mappingService.dataObjectFromDataDom(path, result); + } catch (DeserializationException e) { throw new IllegalStateException(e); } @@ -116,7 +132,7 @@ public class BindingIndependentDataServiceConnector implements // DataObject baData = mappingService.dataObjectFromDataDom(baKey, entry.getValue()); target.putConfigurationData(baKey, baData); } catch (DeserializationException e) { - LOG.error("Ommiting from BA transaction: {}. Reason {}.", entry.getKey(), e); + LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e); } } for (Entry entry : source @@ -127,7 +143,7 @@ public class BindingIndependentDataServiceConnector implements // DataObject baData = mappingService.dataObjectFromDataDom(baKey, entry.getValue()); target.putOperationalData(baKey, baData); } catch (DeserializationException e) { - LOG.error("Ommiting from BA transaction: {}. Reason {}.", entry.getKey(), e); + LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e); } } for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) { @@ -136,7 +152,7 @@ public class BindingIndependentDataServiceConnector implements // InstanceIdentifier baEntry = mappingService.fromDataDom(entry); target.removeConfigurationData(baEntry); } catch (DeserializationException e) { - LOG.error("Ommiting from BA transaction: {}. Reason {}.", entry, e); + LOG.error("Ommiting from BA transaction: {}.", entry, e); } } for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedOperationalData()) { @@ -145,7 +161,7 @@ public class BindingIndependentDataServiceConnector implements // InstanceIdentifier baEntry = mappingService.fromDataDom(entry); target.removeOperationalData(baEntry); } catch (DeserializationException e) { - LOG.error("Ommiting from BA transaction: {}. Reason{}.", entry, e); + LOG.error("Ommiting from BA transaction: {}.", entry, e); } } return target; diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang b/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang index 9da073f71b..b040aa025e 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang +++ b/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang @@ -121,7 +121,9 @@ module opendaylight-sal-binding-broker-impl { augment "/config:modules/config:module/config:state" { case binding-data-broker { when "/config:modules/config:module/config:type = 'binding-data-broker'"; - uses common:data-state; + container data { + uses common:data-state; + } } } augment "/config:modules/config:module/config:state" { diff --git a/opendaylight/md-sal/sal-binding-dom-it/pom.xml b/opendaylight/md-sal/sal-binding-dom-it/pom.xml index 9a143d3f00..6525fa078e 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/pom.xml +++ b/opendaylight/md-sal/sal-binding-dom-it/pom.xml @@ -49,7 +49,7 @@ - + org.opendaylight.controller sal-binding-broker-impl 1.0-SNAPSHOT @@ -78,5 +78,12 @@ junit junit + + + org.slf4j + slf4j-simple + test + 1.7.2 + diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/PutAugmentationTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/PutAugmentationTest.java new file mode 100644 index 0000000000..96d0361b1d --- /dev/null +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/PutAugmentationTest.java @@ -0,0 +1,176 @@ +package org.opendaylight.controller.sal.binding.test.bugfix; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; +import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest; +import org.opendaylight.controller.sal.binding.api.data.DataChangeListener; +import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActions; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActionsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.supported.actions.ActionType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.supported.actions.ActionTypeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.SupportType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesKey; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; + +import static org.junit.Assert.*; + +public class PutAugmentationTest extends AbstractDataServiceTest implements DataChangeListener { + + private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id"); + private static final String NODE_ID = "openflow:1"; + + private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID)); + + private static final Map NODE_KEY_BI = Collections. singletonMap(NODE_ID_QNAME, + NODE_ID); + + private static final InstanceIdentifier NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) // + .toInstance(); + + + private static final InstanceIdentifier NODE_INSTANCE_ID_BA = InstanceIdentifier// + .builder(NODES_INSTANCE_ID_BA) // + .child(Node.class, NODE_KEY).toInstance(); + + + private static final InstanceIdentifier SUPPORTED_ACTIONS_INSTANCE_ID_BA = InstanceIdentifier// + .builder(NODES_INSTANCE_ID_BA) // + .child(Node.class, NODE_KEY) // + .augmentation(FlowCapableNode.class) // + .child(SupportedActions.class) + .toInstance(); + + + private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier NODE_INSTANCE_ID_BI = // + org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() // + .node(Nodes.QNAME) // + .nodeWithKey(Node.QNAME, NODE_KEY_BI) // + .toInstance(); + private static final QName SUPPORTED_ACTIONS_QNAME = QName.create(FlowCapableNode.QNAME, SupportedActions.QNAME.getLocalName()); + + + private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier SUPPORTED_ACTIONS_INSTANCE_ID_BI = // + org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() // + .node(Nodes.QNAME) // + .nodeWithKey(Node.QNAME, NODE_KEY_BI) // + .node(SUPPORTED_ACTIONS_QNAME) // + .toInstance(); + + private DataChangeEvent, DataObject> receivedChangeEvent; + + + + /** + * Test for Bug 148 + * + * @throws Exception + */ + @Test + public void putNodeAndAugmentation() throws Exception { + + baDataService.registerDataChangeListener(NODES_INSTANCE_ID_BA, this); + + NodeBuilder nodeBuilder = new NodeBuilder(); + nodeBuilder.setId(new NodeId(NODE_ID)); + nodeBuilder.setKey(NODE_KEY); + DataModificationTransaction baseTransaction = baDataService.beginTransaction(); + baseTransaction.putOperationalData(NODE_INSTANCE_ID_BA, nodeBuilder.build()); + RpcResult result = baseTransaction.commit().get(); + assertEquals(TransactionStatus.COMMITED, result.getResult()); + assertNotNull(receivedChangeEvent); + Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA); + assertNotNull(node); + assertEquals(NODE_KEY, node.getKey()); + + FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder(); + fnub.setHardware("Hardware Foo"); + fnub.setManufacturer("Manufacturer Foo"); + fnub.setSerialNumber("Serial Foo"); + fnub.setDescription("Description Foo"); + fnub.setSoftware("JUnit emulated"); + FlowCapableNode fnu = fnub.build(); + InstanceIdentifier augmentIdentifier = InstanceIdentifier.builder(NODE_INSTANCE_ID_BA).augmentation(FlowCapableNode.class).toInstance(); + DataModificationTransaction augmentedTransaction = baDataService.beginTransaction(); + augmentedTransaction.putOperationalData(augmentIdentifier, fnu); + + result = augmentedTransaction.commit().get(); + assertEquals(TransactionStatus.COMMITED, result.getResult()); + + + Node augmentedNode = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA); + assertNotNull(node); + assertEquals(NODE_KEY, augmentedNode.getKey()); + System.out.println("Before assertion"); + assertNotNull(augmentedNode.getAugmentation(FlowCapableNode.class)); + FlowCapableNode readedAugmentation = augmentedNode.getAugmentation(FlowCapableNode.class); + assertEquals(fnu.getDescription(), readedAugmentation.getDescription()); + assertBindingIndependentVersion(NODE_INSTANCE_ID_BI); + testNodeRemove(); + } + + + private void testNodeRemove() throws Exception { + DataModificationTransaction transaction = baDataService.beginTransaction(); + transaction.removeOperationalData(NODE_INSTANCE_ID_BA); + RpcResult result = transaction.commit().get(); + assertEquals(TransactionStatus.COMMITED, result.getResult()); + + Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA); + assertNull(node); + } + + private void verifyNodes(Nodes nodes,Node original) { + assertNotNull(nodes); + assertNotNull(nodes.getNode()); + assertEquals(1, nodes.getNode().size()); + Node readedNode = nodes.getNode().get(0); + assertEquals(original.getId(), readedNode.getId()); + assertEquals(original.getKey(), readedNode.getKey()); + + FlowCapableNode fnu = original.getAugmentation(FlowCapableNode.class); + FlowCapableNode readedAugment = readedNode.getAugmentation(FlowCapableNode.class); + assertNotNull(fnu); + assertEquals(fnu.getDescription(), readedAugment.getDescription()); + assertEquals(fnu.getSerialNumber(), readedAugment.getSerialNumber()); + + } + + private void assertBindingIndependentVersion( + org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) { + CompositeNode node = biDataService.readOperationalData(nodeId); + assertNotNull(node); + } + + private Nodes checkForNodes() { + return (Nodes) baDataService.readOperationalData(NODES_INSTANCE_ID_BA); + } + + @Override + public void onDataChanged(DataChangeEvent, DataObject> change) { + receivedChangeEvent = change; + } + +} diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/ChangeOriginatedInDomBrokerTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/ChangeOriginatedInDomBrokerTest.java index 7fe5f0c5be..7111501b53 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/ChangeOriginatedInDomBrokerTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/ChangeOriginatedInDomBrokerTest.java @@ -116,6 +116,7 @@ public class ChangeOriginatedInDomBrokerTest extends AbstractDataServiceTest { @Test public void simpleModifyOperation() throws Exception { + assertNull(biDataService.readConfigurationData(FLOW_INSTANCE_ID_BI)); registerCommitHandler(); @@ -123,7 +124,7 @@ public class ChangeOriginatedInDomBrokerTest extends AbstractDataServiceTest { DataModificationTransaction biTransaction = biDataService.beginTransaction(); biTransaction.putConfigurationData(FLOW_INSTANCE_ID_BI, domflow); RpcResult biResult = biTransaction.commit().get(); - + assertEquals(TransactionStatus.COMMITED, biResult.getResult()); assertNotNull(modificationCapture); Flow flow = (Flow) modificationCapture.getCreatedConfigurationData().get(FLOW_INSTANCE_ID_BA); assertNotNull(flow); diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java index e46b566522..54e1a065f4 100644 --- a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.sal.common.util; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -20,7 +21,7 @@ public class Rpcs { return ret; } - private static class RpcResultTO implements RpcResult { + private static class RpcResultTO implements RpcResult, Serializable { private final Collection errors; private final T result; diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractConsumer.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractConsumer.java index 1fb73bc9a9..99a38ca43a 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractConsumer.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractConsumer.java @@ -13,17 +13,24 @@ import java.util.Collections; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; -public abstract class AbstractConsumer implements Consumer, BundleActivator { +public abstract class AbstractConsumer implements Consumer, BundleActivator,ServiceTrackerCustomizer { + + + + + private BundleContext context; + private ServiceTracker tracker; + private Broker broker; - Broker broker; - ServiceReference brokerRef; @Override public final void start(BundleContext context) throws Exception { + this.context = context; this.startImpl(context); - brokerRef = context.getServiceReference(Broker.class); - broker = context.getService(brokerRef); - broker.registerConsumer(this,context); + tracker = new ServiceTracker<>(context, Broker.class, this); + tracker.open(); } @@ -32,9 +39,7 @@ public abstract class AbstractConsumer implements Consumer, BundleActivator { public final void stop(BundleContext context) throws Exception { stopImpl(context); broker = null; - if(brokerRef != null) { - context.ungetService(brokerRef); - } + tracker.close(); } protected void startImpl(BundleContext context) { @@ -49,4 +54,25 @@ public abstract class AbstractConsumer implements Consumer, BundleActivator { return Collections.emptySet(); } + + @Override + public Broker addingService(ServiceReference reference) { + if(broker == null) { + broker = context.getService(reference); + broker.registerConsumer(this, context); + return broker; + } + + return null; + } + + @Override + public void modifiedService(ServiceReference reference, Broker service) { + // NOOP + } + + @Override + public void removedService(ServiceReference reference, Broker service) { + stopImpl(context); + } } diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractProvider.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractProvider.java index 621ef92132..1cb1a2bc85 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractProvider.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractProvider.java @@ -10,16 +10,20 @@ package org.opendaylight.controller.sal.core.api; import java.util.Collection; import java.util.Collections; +import javax.naming.Context; + import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; -public abstract class AbstractProvider implements BundleActivator, Provider { +public abstract class AbstractProvider implements BundleActivator, Provider,ServiceTrackerCustomizer { - private ServiceReference brokerRef; private Broker broker; - + private BundleContext context; + private ServiceTracker tracker; @Override public Collection getProviderFunctionality() { return Collections.emptySet(); @@ -27,12 +31,10 @@ public abstract class AbstractProvider implements BundleActivator, Provider { @Override public final void start(BundleContext context) throws Exception { - brokerRef = context.getServiceReference(Broker.class); - broker = context.getService(brokerRef); - + this.context = context; this.startImpl(context); - - broker.registerProvider(this,context); + tracker = new ServiceTracker<>(context, Broker.class, this); + tracker.open(); } protected void startImpl(BundleContext context) { @@ -44,7 +46,31 @@ public abstract class AbstractProvider implements BundleActivator, Provider { @Override public final void stop(BundleContext context) throws Exception { + broker = null; + tracker.close(); + tracker = null; stopImpl(context); } + @Override + public Broker addingService(ServiceReference reference) { + if(broker == null) { + broker = context.getService(reference); + broker.registerProvider(this, context); + return broker; + } + + return null; + } + + @Override + public void modifiedService(ServiceReference reference, Broker service) { + // NOOP + } + + @Override + public void removedService(ServiceReference reference, Broker service) { + stopImpl(context); + } + } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java index 050966faa0..9a2a90445e 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java @@ -9,10 +9,12 @@ */ package org.opendaylight.controller.config.yang.md.sal.dom.impl; +import org.opendaylight.controller.config.yang.md.sal.dom.statistics.DomBrokerRuntimeMXBeanImpl; import org.opendaylight.controller.sal.core.api.data.DataStore; import org.opendaylight.controller.sal.dom.broker.BrokerConfigActivator; import org.opendaylight.controller.sal.dom.broker.BrokerImpl; import org.osgi.framework.BundleContext; + import static com.google.common.base.Preconditions.*; /** @@ -37,14 +39,15 @@ public final class DomBrokerImplModule extends org.opendaylight.controller.confi checkArgument(getDataStore() != null, "Data Store needs to be provided for DomBroker"); } - - @Override public java.lang.AutoCloseable createInstance() { - BrokerImpl broker = new BrokerImpl(); - BrokerConfigActivator activator = new BrokerConfigActivator(); - DataStore store = getDataStoreDependency(); - activator.start(broker, store,getBundleContext()); + final BrokerImpl broker = new BrokerImpl(); + final BrokerConfigActivator activator = new BrokerConfigActivator(); + final DataStore store = getDataStoreDependency(); + activator.start(broker, store, getBundleContext()); + + final DomBrokerImplRuntimeMXBean domBrokerRuntimeMXBean = new DomBrokerRuntimeMXBeanImpl(activator.getDataService()); + getRootRuntimeBeanRegistratorWrapper().register(domBrokerRuntimeMXBean); return broker; } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/statistics/DomBrokerRuntimeMXBeanImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/statistics/DomBrokerRuntimeMXBeanImpl.java new file mode 100644 index 0000000000..bc13979d67 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/statistics/DomBrokerRuntimeMXBeanImpl.java @@ -0,0 +1,36 @@ +package org.opendaylight.controller.config.yang.md.sal.dom.statistics; + +import org.opendaylight.controller.config.yang.md.sal.dom.impl.Data; +import org.opendaylight.controller.config.yang.md.sal.dom.impl.DomBrokerImplRuntimeMXBean; +import org.opendaylight.controller.config.yang.md.sal.dom.impl.Transactions; +import org.opendaylight.controller.sal.dom.broker.DataBrokerImpl; + +public class DomBrokerRuntimeMXBeanImpl implements + DomBrokerImplRuntimeMXBean { + + private final DataBrokerImpl dataService; + private final Transactions transactions = new Transactions(); + private final Data data = new Data(); + + public DomBrokerRuntimeMXBeanImpl(DataBrokerImpl dataService) { + this.dataService = dataService; + } + + public Transactions getTransactions() { + transactions.setCreated(dataService.getCreatedTransactionsCount().get()); + transactions.setSubmitted(dataService.getSubmittedTransactionsCount().get()); + transactions.setSuccessful(dataService.getFinishedTransactionsCount().get()); + transactions.setFailed(dataService.getFailedTransactionsCount().get()); + return transactions; + } + + @Override + public Data getData() { + transactions.setCreated(dataService.getCreatedTransactionsCount().get()); + transactions.setSubmitted(dataService.getSubmittedTransactionsCount().get()); + transactions.setSuccessful(dataService.getFinishedTransactionsCount().get()); + transactions.setFailed(dataService.getFailedTransactionsCount().get()); + data.setTransactions(transactions); + return data; + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.xtend index da7cccb156..dc116ca979 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.xtend +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.xtend @@ -14,20 +14,22 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier import org.opendaylight.controller.sal.core.api.data.DataStore import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener +import org.opendaylight.controller.sal.dom.broker.impl.RpcRouterImpl class BrokerConfigActivator implements AutoCloseable { private static val ROOT = InstanceIdentifier.builder().toInstance(); + + @Property + private var DataBrokerImpl dataService; private var ServiceRegistration schemaReg; private var ServiceRegistration dataReg; private var ServiceRegistration dataProviderReg; private var ServiceRegistration mountReg; private var ServiceRegistration mountProviderReg; - private var SchemaServiceImpl schemaService; - private var DataBrokerImpl dataService; private var MountPointManagerImpl mountService; SchemaAwareDataStoreAdapter wrappedStore @@ -36,7 +38,7 @@ class BrokerConfigActivator implements AutoCloseable { val emptyProperties = new Hashtable(); broker.setBundleContext(context); - + broker.setRouter(new RpcRouterImpl("Rpc router")) schemaService = new SchemaServiceImpl(); schemaService.setContext(context); schemaService.setParser(new YangParserImpl()); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/DataBrokerImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/DataBrokerImpl.java index ac5313a9ca..56eae97848 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/DataBrokerImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/DataBrokerImpl.java @@ -2,7 +2,6 @@ package org.opendaylight.controller.sal.dom.broker; import java.util.concurrent.atomic.AtomicLong; -import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.DataReader; import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataBroker; import org.opendaylight.controller.sal.common.DataStoreIdentifier; @@ -15,17 +14,23 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; public class DataBrokerImpl extends AbstractDataBroker implements - DataProviderService { + DataProviderService, AutoCloseable { + private AtomicLong nextTransaction = new AtomicLong(); + private final AtomicLong createdTransactionsCount = new AtomicLong(); + public DataBrokerImpl() { setDataReadRouter(new DataReaderRouter()); } - - private AtomicLong nextTransaction = new AtomicLong(); + + public AtomicLong getCreatedTransactionsCount() { + return createdTransactionsCount; + } @Override public DataTransactionImpl beginTransaction() { String transactionId = "DOM-" + nextTransaction.getAndIncrement(); + createdTransactionsCount.getAndIncrement(); return new DataTransactionImpl(transactionId,this); } @@ -66,4 +71,9 @@ public class DataBrokerImpl extends AbstractDataBroker, RpcImplementation, AutoCloseable { +class NetconfDevice implements + Provider, // + DataReader, // + DataCommitHandler, // + RpcImplementation, // + AutoCloseable { var NetconfClient client; @@ -36,14 +44,17 @@ class NetconfDevice implements Provider, DataReader> operReaderReg + @Property + var ReconnectStrategy strategy; + Registration> operReaderReg Registration> confReaderReg - - String name - + Registration> commitHandlerReg + + val String name MountProvisionService mountService - + + public new(String name) { this.name = name; this.path = InstanceIdentifier.builder(INVENTORY_PATH).nodeWithKey(INVENTORY_NODE, @@ -51,13 +62,14 @@ class NetconfDevice implements Provider, DataReader modification) { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } + override close() { confReaderReg?.close() operReaderReg?.close() diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend index d23ec1cd61..78f6d59f77 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend @@ -18,6 +18,8 @@ import java.util.concurrent.atomic.AtomicInteger import org.w3c.dom.Document import org.w3c.dom.Element import org.opendaylight.controller.sal.common.util.Rpcs +import java.util.List +import com.google.common.collect.ImmutableList class NetconfMapping { @@ -26,12 +28,18 @@ class NetconfMapping { public static val NETCONF_RPC_QNAME = new QName(NETCONF_QNAME,"rpc"); public static val NETCONF_GET_QNAME = new QName(NETCONF_QNAME,"get"); public static val NETCONF_GET_CONFIG_QNAME = new QName(NETCONF_QNAME,"get-config"); + public static val NETCONF_SOURCE_QNAME = new QName(NETCONF_QNAME,"source"); + public static val NETCONF_RUNNING_QNAME = new QName(NETCONF_QNAME,"running"); public static val NETCONF_RPC_REPLY_QNAME = new QName(NETCONF_QNAME,"rpc-reply"); public static val NETCONF_OK_QNAME = new QName(NETCONF_QNAME,"ok"); public static val NETCONF_DATA_QNAME = new QName(NETCONF_QNAME,"data"); + static List> RUNNING = Collections.>singletonList(new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME,null,null)); + public static val CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME,null,RUNNING); static val messageId = new AtomicInteger(0); + + @@ -88,6 +96,15 @@ class NetconfMapping { } } + static def wrap(QName name,Node additional,Node node) { + if(node != null) { + return new CompositeNodeTOImpl(name,null,ImmutableList.of(additional,node)); + } + else { + return new CompositeNodeTOImpl(name,null,ImmutableList.of(additional)); + } + } + public static def Node toCompositeNode(Document document) { return XmlDocumentUtils.toCompositeNode(document) as Node diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/implementation/pom.xml new file mode 100644 index 0000000000..b8e0938a5f --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/pom.xml @@ -0,0 +1,199 @@ + + + 4.0.0 + + org.opendaylight.controller + sal-parent + ../.. + 1.0-SNAPSHOT + + + sal-remoterpc-connector + bundle + + + 0.3.1 + 1.9.8 + 1.0.1 + + + + + + ${project.groupId} + sal-core-api + ${project.version} + + + ${project.groupId} + sal-connector-api + ${project.version} + + + ${project.groupId} + sal-common-util + ${project.version} + + + org.opendaylight.controller + zeromq-routingtable.implementation + + 0.4.1-SNAPSHOT + + + + + org.opendaylight.controller + sal + + + + + org.opendaylight.yangtools + yang-common + + + org.opendaylight.yangtools + yang-data-api + + + org.opendaylight.yangtools + yang-data-impl + + + org.opendaylight.yangtools + yang-common + + + + + org.osgi + org.osgi.core + + + org.zeromq + jeromq + ${zeromq.version} + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + org.codehaus.jackson + jackson-core-asl + ${jackson.version} + + + org.codehaus.jackson + jackson-mapper-asl + ${jackson.version} + + + stax + stax-api + ${stax.version} + + + + + junit + junit + + + org.mockito + mockito-all + + + org.powermock + powermock-module-junit4 + + + org.powermock + powermock-api-mockito + + + org.powermock + powermock-core + + + + + + + + + org.apache.felix + maven-bundle-plugin + ${bundle.plugin.version} + true + + + + *, + !org.codehaus.enunciate.jaxrs + + + org.opendaylight.controller.config.yang.md.sal.remote.rpc, + org.opendaylight.controller.sal.connector.remoterpc, + org.opendaylight.controller.sal.connector.remoterpc.* + + ${project.groupId}.${project.artifactId} + + + + + + + org.opendaylight.yangtools + yang-maven-plugin + 0.5.9-SNAPSHOT + + + + generate-sources + + + + + + org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + + ${project.build.directory}/generated-sources/config + + + urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang + + + + + org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl + target/site/models + + + true + + + + + + org.opendaylight.controller + yang-jmx-generator-plugin + 0.2.3-SNAPSHOT + + + org.opendaylight.yangtools + maven-sal-api-gen-plugin + 0.6.0-SNAPSHOT + jar + + + + + + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/config/yang/md/sal/remote/rpc/ZeroMQServerModule.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/config/yang/md/sal/remote/rpc/ZeroMQServerModule.java new file mode 100644 index 0000000000..606f282bd7 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/config/yang/md/sal/remote/rpc/ZeroMQServerModule.java @@ -0,0 +1,66 @@ +/** +* Generated file + +* Generated from: yang module name: odl-sal-dom-rpc-remote-cfg yang module local name: remote-zeromq-rpc-server +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Thu Dec 05 14:25:21 CET 2013 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.controller.config.yang.md.sal.remote.rpc; + +import org.opendaylight.controller.sal.connector.remoterpc.Client; +import org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcProvider; +import org.opendaylight.controller.sal.connector.remoterpc.RoutingTableProvider; +import org.opendaylight.controller.sal.connector.remoterpc.ServerImpl; +import org.opendaylight.controller.sal.core.api.Broker; +import org.osgi.framework.BundleContext; + +/** +* +*/ +public final class ZeroMQServerModule extends org.opendaylight.controller.config.yang.md.sal.remote.rpc.AbstractZeroMQServerModule + { + + private static final Integer ZEROMQ_ROUTER_PORT = 5554; + private BundleContext bundleContext; + + public ZeroMQServerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public ZeroMQServerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, + ZeroMQServerModule oldModule, java.lang.AutoCloseable oldInstance) { + + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + protected void customValidation(){ + // Add custom validation for module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + + Broker broker = getDomBrokerDependency(); + RoutingTableProvider provider = new RoutingTableProvider(bundleContext); + + + final int port = getPort() != null ? getPort() : ZEROMQ_ROUTER_PORT; + + ServerImpl serverImpl = new ServerImpl(port); + + Client clientImpl = new Client(); + RemoteRpcProvider facade = new RemoteRpcProvider(serverImpl, clientImpl); + + facade.setRoutingTableProvider(provider ); + + broker.registerProvider(facade, bundleContext); + return facade; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/config/yang/md/sal/remote/rpc/ZeroMQServerModuleFactory.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/config/yang/md/sal/remote/rpc/ZeroMQServerModuleFactory.java new file mode 100644 index 0000000000..3cc3ac0f68 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/config/yang/md/sal/remote/rpc/ZeroMQServerModuleFactory.java @@ -0,0 +1,37 @@ +/** +* Generated file + +* Generated from: yang module name: odl-sal-dom-rpc-remote-cfg yang module local name: remote-zeromq-rpc-server +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Thu Dec 05 14:25:21 CET 2013 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.controller.config.yang.md.sal.remote.rpc; + +import org.opendaylight.controller.config.api.DependencyResolver; +import org.opendaylight.controller.config.api.DynamicMBeanWithInstance; +import org.opendaylight.controller.config.spi.Module; +import org.osgi.framework.BundleContext; + +/** +* +*/ +public class ZeroMQServerModuleFactory extends org.opendaylight.controller.config.yang.md.sal.remote.rpc.AbstractZeroMQServerModuleFactory +{ + + @Override + public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) { + ZeroMQServerModule module = (ZeroMQServerModule) super.createModule(instanceName, dependencyResolver, bundleContext); + module.setBundleContext(bundleContext); + return module; + } + + @Override + public Module createModule(String instanceName, DependencyResolver dependencyResolver, + DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception { + ZeroMQServerModule module = (ZeroMQServerModule) super.createModule(instanceName, dependencyResolver, old,bundleContext); + module.setBundleContext(bundleContext); + return module; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/Client.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/Client.java new file mode 100644 index 0000000000..ef3162359c --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/Client.java @@ -0,0 +1,188 @@ +/* + * 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.connector.remoterpc; + +import com.google.common.base.Optional; + +import org.opendaylight.controller.sal.common.util.RpcErrors; +import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.sal.connector.api.RpcRouter; +import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable; +import org.opendaylight.controller.sal.connector.remoterpc.dto.Message; +import org.opendaylight.controller.sal.connector.remoterpc.dto.MessageWrapper; +import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl; +import org.opendaylight.controller.sal.connector.remoterpc.util.XmlUtils; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zeromq.ZMQ; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.*; + +import static com.google.common.base.Preconditions.*; + +/** + * An implementation of {@link RpcImplementation} that makes remote RPC calls + */ +public class Client implements RemoteRpcClient { + + private final Logger _logger = LoggerFactory.getLogger(Client.class); + + private final LinkedBlockingQueue requestQueue = new LinkedBlockingQueue(100); + + private final ExecutorService pool = Executors.newSingleThreadExecutor(); + private final long TIMEOUT = 5000; // in ms + + private RoutingTableProvider routingTableProvider; + + public RoutingTableProvider getRoutingTableProvider() { + return routingTableProvider; + } + + public void setRoutingTableProvider(RoutingTableProvider routingTableProvider) { + this.routingTableProvider = routingTableProvider; + } + + public LinkedBlockingQueue getRequestQueue() { + return requestQueue; + } + + @Override + public Set getSupportedRpcs() { + // TODO: Find the entries from routing table + return Collections.emptySet(); + } + + public void start() { + pool.execute(new Sender(this)); + + } + + public void stop() { + + _logger.debug("Client stopping..."); + Context.getInstance().getZmqContext().term(); + _logger.debug("ZMQ context terminated"); + + pool.shutdown(); // intiate shutdown + try { + if (!pool.awaitTermination(10, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(10, TimeUnit.SECONDS)) + _logger.error("Client thread pool did not shut down"); + } + } catch (InterruptedException e) { + // (Re-)Cancel if current thread also interrupted + pool.shutdownNow(); + // Preserve interrupt status + Thread.currentThread().interrupt(); + } + _logger.debug("Client stopped"); + } + + @Override + public RpcResult invokeRpc(QName rpc, CompositeNode input) { + + RouteIdentifierImpl routeId = new RouteIdentifierImpl(); + routeId.setType(rpc); + + String address = lookupRemoteAddress(routeId); + + Message request = new Message.MessageBuilder().type(Message.MessageType.REQUEST) + .sender(Context.getInstance().getLocalUri()).recipient(address).route(routeId) + .payload(XmlUtils.compositeNodeToXml(input)).build(); + + List errors = new ArrayList(); + + try (SocketPair pair = new SocketPair()) { + + MessageWrapper messageWrapper = new MessageWrapper(request, pair.getSender()); + process(messageWrapper); + Message response = parseMessage(pair.getReceiver()); + + CompositeNode payload = XmlUtils.xmlToCompositeNode((String) response.getPayload()); + + return Rpcs.getRpcResult(true, payload, errors); + + } catch (Exception e) { + collectErrors(e, errors); + return Rpcs.getRpcResult(false, null, errors); + } + + } + + public void process(MessageWrapper msg) throws TimeoutException, InterruptedException { + _logger.debug("Processing message [{}]", msg); + + boolean success = requestQueue.offer(msg, TIMEOUT, TimeUnit.MILLISECONDS); + if (!success) + throw new TimeoutException("Queue is full"); + } + + /** + * Block on socket for reply + * + * @param receiver + * @return + */ + private Message parseMessage(ZMQ.Socket receiver) throws IOException, ClassNotFoundException { + return (Message) Message.deserialize(receiver.recv()); + } + + /** + * Find address for the given route identifier in routing table + * + * @param routeId + * route identifier + * @return remote network address + */ + private String lookupRemoteAddress(RpcRouter.RouteIdentifier routeId) { + checkNotNull(routeId, "route must not be null"); + + Optional> routingTable = routingTableProvider.getRoutingTable(); + checkNotNull(routingTable.isPresent(), "Routing table is null"); + + Set addresses = routingTable.get().getRoutes(routeId.toString()); + checkNotNull(addresses, "Address not found for route [%s]", routeId); + checkState(addresses.size() == 1, "Multiple remote addresses found for route [%s], \nonly 1 expected", routeId); // its + // a + // global + // service. + + String address = addresses.iterator().next(); + checkNotNull(address, "Address not found for route [%s]", routeId); + + return address; + } + + private void collectErrors(Exception e, List errors) { + if (e == null) + return; + if (errors == null) + errors = new ArrayList(); + + errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause())); + for (Throwable t : e.getSuppressed()) { + errors.add(RpcErrors.getRpcError(null, null, null, null, t.getMessage(), null, t)); + } + } + + @Override + public void close() throws Exception { + stop(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/Context.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/Context.java new file mode 100644 index 0000000000..f0bf12cbc0 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/Context.java @@ -0,0 +1,91 @@ +/* + * 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.connector.remoterpc; + +import org.zeromq.ZMQ; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +/** + * Provides a ZeroMQ Context object + */ +public class Context { + private ZMQ.Context zmqContext = ZMQ.context(1); + private String uri; + + private static Context _instance = new Context(); + + private Context() {} + + public static Context getInstance(){ + return _instance; + } + + public ZMQ.Context getZmqContext(){ + return this.zmqContext; + } + + public String getLocalUri(){ + uri = (uri != null) ? uri + : new StringBuilder("tcp://").append(getIpAddress()).append(":") + .append(getRpcPort()).toString(); + + return uri; + } + + public String getRpcPort(){ + String rpcPort = (System.getProperty("rpc.port") != null) + ? System.getProperty("rpc.port") + : "5554"; + + return rpcPort; + } + + private String getIpAddress(){ + String ipAddress = (System.getProperty("local.ip") != null) + ? System.getProperty("local.ip") + : findIpAddress(); + + return ipAddress; + } + + /** + * Finds IPv4 address of the local VM + * TODO: This method is non-deterministic. There may be more than one IPv4 address. Cant say which + * address will be returned. Read IP from a property file or enhance the code to make it deterministic. + * Should we use IP or hostname? + * + * @return + */ + private String findIpAddress() { + String hostAddress = null; + Enumeration e = null; + try { + e = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e1) { + e1.printStackTrace(); + } + while (e.hasMoreElements()) { + + NetworkInterface n = (NetworkInterface) e.nextElement(); + + Enumeration ee = n.getInetAddresses(); + while (ee.hasMoreElements()) { + InetAddress i = (InetAddress) ee.nextElement(); + if ((i instanceof Inet4Address) && (i.isSiteLocalAddress())) + hostAddress = i.getHostAddress(); + } + } + return hostAddress; + + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RemoteRpcClient.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RemoteRpcClient.java new file mode 100644 index 0000000000..6bd123b7e4 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RemoteRpcClient.java @@ -0,0 +1,13 @@ +package org.opendaylight.controller.sal.connector.remoterpc; + +import org.opendaylight.controller.sal.core.api.RpcImplementation; + +public interface RemoteRpcClient extends RpcImplementation,AutoCloseable{ + + + void setRoutingTableProvider(RoutingTableProvider provider); + + void stop(); + + void start(); +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RemoteRpcProvider.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RemoteRpcProvider.java new file mode 100644 index 0000000000..3c2e3b0872 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RemoteRpcProvider.java @@ -0,0 +1,95 @@ +package org.opendaylight.controller.sal.connector.remoterpc; + +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.ExecutorService; + +import org.opendaylight.controller.sal.connector.remoterpc.Client; +import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable; +import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; +import org.opendaylight.controller.sal.core.api.Provider; +import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; + +public class RemoteRpcProvider implements + RemoteRpcServer, + RemoteRpcClient, + Provider { + + private final ServerImpl server; + private final Client client; + private RoutingTableProvider provider; + + @Override + public void setRoutingTableProvider(RoutingTableProvider provider) { + this.provider = provider; + server.setRoutingTableProvider(provider); + client.setRoutingTableProvider(provider); + } + + @Override + public RpcResult invokeRpc(QName rpc, CompositeNode input) { + return client.invokeRpc(rpc, input); + } + + @Override + public Set getSupportedRpcs() { + return client.getSupportedRpcs(); + } + + + public RemoteRpcProvider(ServerImpl server, Client client) { + this.server = server; + this.client = client; + } + + public void setBrokerSession(ProviderSession session) { + server.setBrokerSession(session); + } + public void setServerPool(ExecutorService serverPool) { + server.setServerPool(serverPool); + } + public void start() { + client.setRoutingTableProvider(provider); + server.setRoutingTableProvider(provider); + server.start(); + client.start(); + } + public void onRouteUpdated(String key, Set values) { + server.onRouteUpdated(key, values); + } + public void onRouteDeleted(String key) { + server.onRouteDeleted(key); + } + + + @Override + public Collection getProviderFunctionality() { + // TODO Auto-generated method stub + return null; + } + + + @Override + public void onSessionInitiated(ProviderSession session) { + server.setBrokerSession(session); + start(); + } + + + public void close() throws Exception { + server.close(); + client.close(); + } + + + + + @Override + public void stop() { + server.stop(); + client.stop(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RemoteRpcServer.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RemoteRpcServer.java new file mode 100644 index 0000000000..932600fcb1 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RemoteRpcServer.java @@ -0,0 +1,6 @@ +package org.opendaylight.controller.sal.connector.remoterpc; + + +public interface RemoteRpcServer extends AutoCloseable { + +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RoutingTableProvider.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RoutingTableProvider.java new file mode 100644 index 0000000000..cfdf98638d --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RoutingTableProvider.java @@ -0,0 +1,32 @@ +package org.opendaylight.controller.sal.connector.remoterpc; + +import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable; +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; + +import com.google.common.base.Optional; + +public class RoutingTableProvider implements AutoCloseable { + + @SuppressWarnings("rawtypes") + final ServiceTracker tracker; + + + public RoutingTableProvider(BundleContext ctx) { + @SuppressWarnings("rawtypes") + ServiceTracker rawTracker = new ServiceTracker<>(ctx, RoutingTable.class, null); + tracker = rawTracker; + tracker.open(); + } + + public Optional> getRoutingTable() { + @SuppressWarnings("unchecked") + RoutingTable tracked = tracker.getService(); + return Optional.fromNullable(tracked); + } + + @Override + public void close() throws Exception { + tracker.close(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RpcSocket.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RpcSocket.java new file mode 100644 index 0000000000..7e8590ab9b --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/RpcSocket.java @@ -0,0 +1,221 @@ +/* + * 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.connector.remoterpc; + +import org.opendaylight.controller.sal.connector.remoterpc.dto.Message; +import org.opendaylight.controller.sal.connector.remoterpc.dto.MessageWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zeromq.ZMQ; + +import java.io.IOException; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A class encapsulating {@link ZMQ.Socket} of type {@link ZMQ.REQ}. + * It adds following capabilities: + *
  • Retry logic - Tries 3 times before giving up + *
  • Request times out after {@link TIMEOUT} property + *
  • The limitation of {@link ZMQ.REQ}/{@link ZMQ.REP} pair is that no 2 requests can be sent before + * the response for the 1st request is received. To overcome that, this socket queues all messages until + * the previous request has been responded. + */ +public class RpcSocket { + + // Constants + public static final int TIMEOUT = 2000; + public static final int QUEUE_SIZE = 10; + public static final int NUM_RETRIES = 3; + private static final Logger log = LoggerFactory.getLogger(RpcSocket.class); + + private ZMQ.Socket socket; + private ZMQ.Poller poller; + private String address; + private SocketState state; + private long sendTime; + private int retriesLeft; + private LinkedBlockingQueue inQueue; + + + public RpcSocket(String address, ZMQ.Poller poller) { + this.socket = null; + this.state = new IdleSocketState(); + this.sendTime = -1; + this.retriesLeft = NUM_RETRIES; + this.inQueue = new LinkedBlockingQueue(QUEUE_SIZE); + this.address = address; + this.poller = poller; + createSocket(); + } + + public ZMQ.Socket getSocket() { + return socket; + } + + public String getAddress() { + return address; + } + + public int getRetriesLeft() { + return retriesLeft; + } + + public void setRetriesLeft(int retriesLeft) { + this.retriesLeft = retriesLeft; + } + + public SocketState getState() { + return state; + } + + public void setState(SocketState state) { + this.state = state; + } + + public int getQueueSize() { + return inQueue.size(); + } + + public MessageWrapper removeCurrentRequest() { + return inQueue.poll(); + } + + public boolean hasTimedOut() { + return (System.currentTimeMillis() - sendTime > RpcSocket.TIMEOUT); + } + + public void send(MessageWrapper request) throws TimeoutException { + try { + boolean success = inQueue.offer(request, TIMEOUT, TimeUnit.MILLISECONDS); + if (!success) { + throw new TimeoutException("send :: Queue is full"); + } + process(); + } + catch (InterruptedException e) { + log.error("send : Thread interrupted while attempting to add request to inQueue", e); + } + } + + public MessageWrapper receive() { + Message response = parseMessage(); + MessageWrapper messageWrapper = inQueue.poll(); //remove the message from queue + MessageWrapper responseMessageWrapper = new MessageWrapper(response, messageWrapper.getReceiveSocket()); + + state = new IdleSocketState(); + retriesLeft = NUM_RETRIES; + return responseMessageWrapper; + } + + public void process() { + if (getQueueSize() > 0) //process if there's message in the queue + state.process(this); + } + + // Called by IdleSocketState & BusySocketState + public void sendMessage() { + //Get the message from queue without removing it. For retries + MessageWrapper messageWrapper = inQueue.peek(); + if (messageWrapper != null) { + Message message = messageWrapper.getMessage(); + try { + socket.send(Message.serialize(message)); + } + catch (IOException e) { + log.debug("Message send failed [{}]", message); + log.debug("Exception [{}]", e); + } + sendTime = System.currentTimeMillis(); + } + } + + public Message parseMessage() { + Message parsedMessage = null; + byte[] bytes = socket.recv(); + log.debug("Received bytes:[{}]", bytes.length); + try { + parsedMessage = (Message)Message.deserialize(bytes); + } + catch (IOException|ClassNotFoundException e) { + log.debug("parseMessage : Deserializing received bytes failed", e); + } + + return parsedMessage; + } + + public void recycleSocket() { + close(); + } + + public void close() { + socket.setLinger(10); + socket.close(); + } + + private void createSocket() { + socket = Context.getInstance().getZmqContext().socket(ZMQ.REQ); + socket.connect(address); + poller.register(socket, ZMQ.Poller.POLLIN); + state = new IdleSocketState(); + } + + + /** + * Represents the state of a {@link org.opendaylight.controller.sal.connector.remoterpc.RpcSocket} + */ + public static interface SocketState { + + /* The processing actions to be performed in this state + */ + public void process(RpcSocket socket); + } + + /** + * Represents the idle state of a {@link org.opendaylight.controller.sal.connector.remoterpc.RpcSocket} + */ + public static class IdleSocketState implements SocketState { + + @Override + public void process(RpcSocket socket) { + socket.sendMessage(); + socket.setState(new BusySocketState()); + socket.setRetriesLeft(socket.getRetriesLeft()-1); + } + } + + /** + * Represents the busy state of a {@link org.opendaylight.controller.sal.connector.remoterpc.RpcSocket} + */ + public static class BusySocketState implements SocketState { + + private static Logger log = LoggerFactory.getLogger(BusySocketState.class); + + @Override + public void process(RpcSocket socket) { + if (socket.hasTimedOut()) { + if (socket.getRetriesLeft() > 0) { + log.debug("process : Request timed out, retrying now..."); + socket.sendMessage(); + socket.setRetriesLeft(socket.getRetriesLeft() - 1); + } + else { + // No more retries for current request, so stop processing the current request + MessageWrapper message = socket.removeCurrentRequest(); + if (message != null) { + log.error("Unable to process rpc request [{}]", message); + socket.setState(new IdleSocketState()); + socket.setRetriesLeft(NUM_RETRIES); + } + } + } + // Else no timeout, so allow processing to continue + } + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/Sender.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/Sender.java new file mode 100644 index 0000000000..f53d5adc1c --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/Sender.java @@ -0,0 +1,218 @@ +/* + * 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.connector.remoterpc; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import org.opendaylight.controller.sal.connector.remoterpc.dto.Message; +import org.opendaylight.controller.sal.connector.remoterpc.dto.MessageWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zeromq.ZMQ; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +import static com.google.common.base.Preconditions.*; + +/** + * Main server thread for sending requests. + */ +public class Sender implements Runnable{ + + private final static Logger _logger = LoggerFactory.getLogger(Sender.class); + private final Client client; + + + + + public Sender(Client client) { + super(); + this.client = client; + } + +@Override + public void run() { + _logger.info("Starting..."); + + try (SocketManager socketManager = new SocketManager()){ + while (!Thread.currentThread().isInterrupted()) { + + //read incoming messages from blocking queue + MessageWrapper request = pollForRequest(); + + if (request != null) { + processRequest(socketManager, request); + } + + flushSockets(socketManager); + pollForResponse(socketManager); + processResponse(socketManager); + + } + } catch(Exception t){ + _logger.error("Exception: [{}]", t); + _logger.error("Stopping..."); + } + } + + private void processResponse(SocketManager socketManager) { + for (int i = 0; i < socketManager.getPoller().getSize(); i++) { + // If any sockets get a response, process it + if (socketManager.getPoller().pollin(i)) { + Optional socket = socketManager.getManagedSocketFor( + socketManager.getPoller().getItem(i).getSocket()); + + checkState(socket.isPresent(), "Managed socket not found"); + + MessageWrapper response = socket.get().receive(); + _logger.debug("Received rpc response [{}]", response.getMessage()); + + //TODO: handle exception and introduce timeout on receiver side + try { + response.getReceiveSocket().send(Message.serialize(response.getMessage())); + } catch (IOException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + } + } + + private void processRequest(SocketManager socketManager, MessageWrapper request) throws TimeoutException { + + if ((request.getMessage() == null) || + (request.getMessage().getRecipient() == null)) { + //invalid message. log and drop + _logger.error("Invalid request [{}]", request); + return; + } + + RpcSocket socket = + socketManager.getManagedSocket(request.getMessage().getRecipient()); + + socket.send(request); + } + + private void flushSockets(SocketManager socketManager){ + for (RpcSocket socket : socketManager.getManagedSockets()){ + socket.process(); + } + } + + private MessageWrapper pollForRequest(){ + return client.getRequestQueue().poll(); + } + + private void pollForResponse(SocketManager socketManager){ + try{ + socketManager.getPoller().poll(10); //poll every 10ms + }catch (Throwable t) { /*Ignore and continue*/ } + } +} + + +/* +SCALA + +package org.opendaylight.controller.sal.connector.remoterpc + + import org.slf4j.{LoggerFactory, Logger} + import scala.collection.JavaConverters._ + import scala.Some + import org.opendaylight.controller.sal.connector.remoterpc.dto.{MessageWrapper, Message} +*/ +/** + * Main server thread for sending requests. This does not maintain any state. If the + * thread dies, it will be restarted + */ +/*class Sender extends Runnable { + private val _logger: Logger = LoggerFactory.getLogger(Sender.this.getClass()) + + override def run = { + _logger.info("Sender starting...") + val socketManager = new SocketManager() + + try { + while (!Thread.currentThread().isInterrupted) { + //read incoming messages from blocking queue + val request: MessageWrapper = Client.requestQueue.poll() + + if (request != null) { + if ((request.message != null) && + (request.message.getRecipient != null)) { + + val socket = socketManager.getManagedSocket(request.message.getRecipient) + socket.send(request) + } else { + //invalid message. log and drop + _logger.error("Invalid request [{}]", request) + } + } + + socketManager.getManagedSockets().asScala.map(s => s.process) + + // Poll all sockets for responses every 1 sec + poll(socketManager) + + // If any sockets get a response, process it + for (i <- 0 until socketManager.poller.getSize) { + if (socketManager.poller.pollin(i)) { + val socket = socketManager.getManagedSocketFor(socketManager.poller.getItem(i).getSocket) + + socket match { + case None => //{ + _logger.error("Could not find a managed socket for zmq socket") + throw new IllegalStateException("Could not find a managed socket for zmq socket") + //} + case Some(s) => { + val response = s.receive() + _logger.debug("Received rpc response [{}]", response.message) + response.receiveSocket.send(Message.serialize(response.message)) + } + } + } + } + + } + } catch{ + case e:Exception => { + _logger.debug("Sender stopping due to exception") + e.printStackTrace() + } + } finally { + socketManager.stop + } + } + + def poll(socketManager:SocketManager) = { + try{ + socketManager.poller.poll(10) + }catch{ + case t:Throwable => //ignore and continue + } + } +} + + +// def newThread(r: Runnable): Thread = { +// val t = new RequestHandler() +// t.setUncaughtExceptionHandler(new RequestProcessorExceptionHandler) +// t +// } + + + +/** + * Restarts the request processing server in the event of unforeseen exceptions + */ +//private class RequestProcessorExceptionHandler extends UncaughtExceptionHandler { +// def uncaughtException(t: Thread, e: Throwable) = { +// _logger.error("Exception caught during request processing [{}]", e) +// _logger.info("Restarting request processor server...") +// RequestProcessor.start() +// } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/ServerImpl.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/ServerImpl.java new file mode 100644 index 0000000000..83b93858cf --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/ServerImpl.java @@ -0,0 +1,285 @@ +/* + * 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.connector.remoterpc; + +import com.google.common.base.Optional; + +import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener; +import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable; +import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException; +import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException; +import org.opendaylight.controller.sal.connector.remoterpc.dto.Message; +import org.opendaylight.controller.sal.connector.remoterpc.dto.Message.MessageType; +import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl; +import org.opendaylight.controller.sal.connector.remoterpc.util.XmlUtils; +import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.controller.sal.core.api.RpcRegistrationListener; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zeromq.ZMQ; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * ZeroMq based implementation of RpcRouter TODO: 1. Make rpc request handling + * async and non-blocking. Note zmq socket is not thread safe 2. Read properties + * from config file using existing(?) ODL properties framework + */ +public class ServerImpl implements RemoteRpcServer, RouteChangeListener { + + private Logger _logger = LoggerFactory.getLogger(ServerImpl.class); + + private ExecutorService serverPool; + + // private RoutingTable routingTable; + private RoutingTableProvider routingTable; + private Set remoteServices; + private ProviderSession brokerSession; + private ZMQ.Context context; + private ZMQ.Socket replySocket; + + private final RpcListener listener = new RpcListener(); + + private final String localUri = Context.getInstance().getLocalUri(); + + private final int rpcPort; + + private RpcImplementation client; + + public RpcImplementation getClient() { + return client; + } + + public void setClient(RpcImplementation client) { + this.client = client; + } + + // Prevent instantiation + public ServerImpl(int rpcPort) { + this.rpcPort = rpcPort; + } + + public void setBrokerSession(ProviderSession session) { + this.brokerSession = session; + } + + public ExecutorService getServerPool() { + return serverPool; + } + + public void setServerPool(ExecutorService serverPool) { + this.serverPool = serverPool; + } + + public void start() { + context = ZMQ.context(1); + serverPool = Executors.newSingleThreadExecutor(); + remoteServices = new HashSet(); + + // Start listening rpc requests + serverPool.execute(receive()); + + brokerSession.addRpcRegistrationListener(listener); + // routingTable.registerRouteChangeListener(routeChangeListener); + + Set currentlySupported = brokerSession.getSupportedRpcs(); + for (QName rpc : currentlySupported) { + listener.onRpcImplementationAdded(rpc); + } + + _logger.debug("RPC Server started [{}]", localUri); + } + + public void stop() { + // TODO: un-subscribe + + // if (context != null) + // context.term(); + // + // _logger.debug("ZMQ Context is terminated."); + + if (serverPool != null) + serverPool.shutdown(); + + _logger.debug("Thread pool is closed."); + } + + private Runnable receive() { + return new Runnable() { + public void run() { + + // Bind to RPC reply socket + replySocket = context.socket(ZMQ.REP); + replySocket.bind("tcp://*:" + Context.getInstance().getRpcPort()); + + // Poller enables listening on multiple sockets using a single + // thread + ZMQ.Poller poller = new ZMQ.Poller(1); + poller.register(replySocket, ZMQ.Poller.POLLIN); + try { + // TODO: Add code to restart the thread after exception + while (!Thread.currentThread().isInterrupted()) { + + poller.poll(); + + if (poller.pollin(0)) { + handleRpcCall(); + } + } + } catch (Exception e) { + // log and continue + _logger.error("Unhandled exception [{}]", e); + } finally { + poller.unregister(replySocket); + replySocket.close(); + } + + } + }; + } + + /** + * @throws InterruptedException + * @throws ExecutionException + */ + private void handleRpcCall() { + + Message request = parseMessage(replySocket); + + _logger.debug("Received rpc request [{}]", request); + + // Call broker to process the message then reply + Future> rpc = null; + RpcResult result = null; + try { + rpc = brokerSession.rpc((QName) request.getRoute().getType(), + XmlUtils.xmlToCompositeNode((String) request.getPayload())); + + result = (rpc != null) ? rpc.get() : null; + + } catch (Exception e) { + _logger.debug("Broker threw [{}]", e); + } + + CompositeNode payload = (result != null) ? result.getResult() : null; + + Message response = new Message.MessageBuilder().type(MessageType.RESPONSE).sender(localUri) + .route(request.getRoute()).payload(XmlUtils.compositeNodeToXml(payload)).build(); + + _logger.debug("Sending rpc response [{}]", response); + + try { + replySocket.send(Message.serialize(response)); + } catch (Exception e) { + _logger.debug("rpc response send failed for message [{}]", response); + _logger.debug("{}", e); + } + + } + + /** + * @param socket + * @return + */ + private Message parseMessage(ZMQ.Socket socket) { + + Message msg = null; + try { + byte[] bytes = socket.recv(); + _logger.debug("Received bytes:[{}]", bytes.length); + msg = (Message) Message.deserialize(bytes); + } catch (Throwable t) { + t.printStackTrace(); + } + return msg; + } + + @Override + public void onRouteUpdated(String key, Set values) { + RouteIdentifierImpl rId = new RouteIdentifierImpl(); + try { + _logger.debug("Updating key/value {}-{}", key, values); + brokerSession.addRpcImplementation((QName) rId.fromString(key).getType(), client); + + } catch (Exception e) { + _logger.info("Route update failed {}", e); + } + } + + @Override + public void onRouteDeleted(String key) { + // TODO: Broker session needs to be updated to support this + throw new UnsupportedOperationException(); + } + + /** + * Listener for rpc registrations + */ + private class RpcListener implements RpcRegistrationListener { + + + + @Override + public void onRpcImplementationAdded(QName name) { + + // if the service name exists in the set, this notice + // has bounced back from the broker. It should be ignored + if (remoteServices.contains(name)) + return; + + _logger.debug("Adding registration for [{}]", name); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(); + routeId.setType(name); + + try { + routingTable.getRoutingTable().get().addGlobalRoute(routeId.toString(), localUri); + _logger.debug("Route added [{}-{}]", name, localUri); + } catch (RoutingTableException | SystemException e) { + // TODO: This can be thrown when route already exists in the + // table. Broker + // needs to handle this. + _logger.error("Unhandled exception while adding global route to routing table [{}]", e); + + } + } + + @Override + public void onRpcImplementationRemoved(QName name) { + + _logger.debug("Removing registration for [{}]", name); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(); + routeId.setType(name); + + try { + routingTable.getRoutingTable().get().removeGlobalRoute(routeId.toString()); + } catch (RoutingTableException | SystemException e) { + _logger.error("Route delete failed {}", e); + } + } + } + + @Override + public void close() throws Exception { + stop(); + } + + public void setRoutingTableProvider(RoutingTableProvider provider) { + this.routingTable = provider; + } + +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/SocketManager.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/SocketManager.java new file mode 100644 index 0000000000..588a299626 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/SocketManager.java @@ -0,0 +1,95 @@ +/* + * 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.connector.remoterpc; + +import com.google.common.base.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zeromq.ZMQ; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * Manages creation of {@link RpcSocket} and their registration with {@link ZMQ.Poller} + */ +public class SocketManager implements AutoCloseable{ + private static final Logger log = LoggerFactory.getLogger(SocketManager.class); + + /* + * RpcSockets mapped by network address its connected to + */ + private ConcurrentHashMap managedSockets = new ConcurrentHashMap(); + + private ZMQ.Poller _poller = new ZMQ.Poller(2); //randomly selected size. Poller grows automatically + + /** + * Returns a {@link RpcSocket} for the given address + * @param address network address with port eg: 10.199.199.20:5554 + * @return + */ + public RpcSocket getManagedSocket(String address) throws IllegalArgumentException { + //Precondition + if (!address.matches("(tcp://)(.*)(:)(\\d*)")) { + throw new IllegalArgumentException("Address must of format 'tcp://:' but is " + address); + } + + if (!managedSockets.containsKey(address)) { + log.debug("{} Creating new socket for {}", Thread.currentThread().getName()); + RpcSocket socket = new RpcSocket(address, _poller); + managedSockets.put(address, socket); + } + + return managedSockets.get(address); + } + + /** + * Returns a {@link RpcSocket} for the given {@link ZMQ.Socket} + * @param socket + * @return + */ + public Optional getManagedSocketFor(ZMQ.Socket socket) { + for (RpcSocket rpcSocket : managedSockets.values()) { + if (rpcSocket.getSocket().equals(socket)) { + return Optional.of(rpcSocket); + } + } + return Optional.absent(); + } + + /** + * Return a collection of all managed sockets + * @return + */ + public Collection getManagedSockets() { + return managedSockets.values(); + } + + /** + * Returns the {@link ZMQ.Poller} + * @return + */ + public ZMQ.Poller getPoller() { + return _poller; + } + + /** + * This should be called when stopping the server to close all the sockets + * @return + */ + @Override + public void close() throws Exception { + log.debug("Stopping..."); + for (RpcSocket socket : managedSockets.values()) { + socket.close(); + } + managedSockets.clear(); + log.debug("Stopped"); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/SocketPair.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/SocketPair.java new file mode 100644 index 0000000000..67b3a830e3 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/SocketPair.java @@ -0,0 +1,41 @@ +package org.opendaylight.controller.sal.connector.remoterpc; + +import org.zeromq.ZMQ; + +import java.util.UUID; + +/** + * + */ +public class SocketPair implements AutoCloseable{ + private ZMQ.Socket sender; + private ZMQ.Socket receiver; + + private static final String INPROC_PREFIX = "inproc://"; + + public SocketPair(){ + String address = new StringBuilder(INPROC_PREFIX) + .append(UUID.randomUUID()) + .toString(); + + receiver = Context.getInstance().getZmqContext().socket(ZMQ.PAIR); + receiver.bind(address); + + sender = Context.getInstance().getZmqContext().socket(ZMQ.PAIR); + sender.connect(address); + } + + public ZMQ.Socket getSender(){ + return this.sender; + } + + public ZMQ.Socket getReceiver(){ + return this.receiver; + } + + @Override + public void close() throws Exception { + sender.close(); + receiver.close(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/CompositeNodeImpl.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/CompositeNodeImpl.java new file mode 100644 index 0000000000..073601a1c0 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/CompositeNodeImpl.java @@ -0,0 +1,153 @@ +/* + * 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.connector.remoterpc.dto; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.*; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class CompositeNodeImpl implements CompositeNode, Serializable { + + private QName key; + private List> children; + + @Override + public List> getChildren() { + return children; + } + + @Override + public List getCompositesByName(QName children) { + throw new UnsupportedOperationException(); + } + + @Override + public List getCompositesByName(String children) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List> getSimpleNodesByName(QName children) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List> getSimpleNodesByName(String children) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public CompositeNode getFirstCompositeByName(QName container) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public SimpleNode getFirstSimpleByName(QName leaf) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public MutableCompositeNode asMutable() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public QName getKey() { + return key; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List> setValue(List> value) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public int size() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isEmpty() { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean containsKey(Object key) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean containsValue(Object value) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List> get(Object key) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List> put(QName key, List> value) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List> remove(Object key) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void putAll(Map>> m) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void clear() { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Set keySet() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Collection>> values() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Set>>> entrySet() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public QName getNodeType() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public CompositeNode getParent() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List> getValue() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public ModifyAction getModificationAction() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/Message.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/Message.java similarity index 88% rename from opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/Message.java rename to opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/Message.java index c2c037aee6..95fe99c81c 100644 --- a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/Message.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/Message.java @@ -6,10 +6,8 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.sal.connector.remoterpc.router.zeromq; +package org.opendaylight.controller.sal.connector.remoterpc.dto; - -import org.codehaus.jackson.map.ObjectMapper; import org.opendaylight.controller.sal.connector.api.RpcRouter; import java.io.*; @@ -20,7 +18,8 @@ public class Message implements Serializable { ANNOUNCE((byte) 0), //TODO: Remove announce, add rpc registration and deregistration HEARTBEAT((byte) 1), REQUEST((byte) 2), - RESPONSE((byte) 3); + RESPONSE((byte) 3), + ERROR((byte)4); private final byte type; @@ -35,6 +34,7 @@ public class Message implements Serializable { private MessageType type; private String sender; + private String recipient; private RpcRouter.RouteIdentifier route; private Object payload; @@ -70,11 +70,19 @@ public class Message implements Serializable { this.payload = payload; } + public String getRecipient() { + return recipient; + } + + public void setRecipient(String recipient) { + this.recipient = recipient; + } @Override public String toString() { return "Message{" + "type=" + type + ", sender='" + sender + '\'' + + ", recipient='" + recipient + '\'' + ", route=" + route + ", payload=" + payload + '}'; @@ -108,17 +116,6 @@ public class Message implements Serializable { return o.readObject(); } - public static byte[] toJsonBytes(Message m) throws IOException { - ObjectMapper o = new ObjectMapper(); - return o.writeValueAsBytes(m); - } - - public static Message fromJsonBytes(byte [] bytes) throws IOException { - - ObjectMapper o = new ObjectMapper(); - return o.readValue(bytes, Message.class); - } - public static class Response extends Message implements RpcRouter.RpcReply { private ResponseCode code; // response code @@ -163,6 +160,11 @@ public class Message implements Serializable { return this; } + public MessageBuilder recipient(String recipient){ + message.setRecipient(recipient); + return this; + } + public MessageBuilder route(RpcRouter.RouteIdentifier route){ message.setRoute(route); return this; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/MessageWrapper.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/MessageWrapper.java new file mode 100644 index 0000000000..8d2198c365 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/MessageWrapper.java @@ -0,0 +1,32 @@ +/* + * 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.connector.remoterpc.dto; + +import org.zeromq.ZMQ; + +/** + * A class encapsulating {@link Message} and the {@link ZMQ.Socket} over which it is transmitted + */ +public class MessageWrapper { + + private Message _message; + private ZMQ.Socket _receiveSocket; + + public MessageWrapper(Message message, ZMQ.Socket receiveSocket) { + this._message = message; + this._receiveSocket = receiveSocket; + } + + public Message getMessage() { + return _message; + } + + public ZMQ.Socket getReceiveSocket() { + return _receiveSocket; + } +} diff --git a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/RouteIdentifierImpl.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/RouteIdentifierImpl.java similarity index 54% rename from opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/RouteIdentifierImpl.java rename to opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/RouteIdentifierImpl.java index ca404f2ca9..6c5e5fbf11 100644 --- a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/RouteIdentifierImpl.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/dto/RouteIdentifierImpl.java @@ -5,19 +5,21 @@ * 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.connector.remoterpc.router.zeromq; +package org.opendaylight.controller.sal.connector.remoterpc.dto; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; import org.opendaylight.controller.sal.connector.api.RpcRouter; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import java.io.Serializable; +import java.net.URI; -/** - * User: abhishk2 - */ public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier,Serializable { + transient ObjectMapper mapper = new ObjectMapper(); + private QName context; private QName type; private InstanceIdentifier route; @@ -51,10 +53,35 @@ public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier dataTree; + try { + dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes())); + } catch (XMLStreamException e) { + _logger.error("Error during building data tree from XML", e); + return null; + } + if (dataTree == null) { + _logger.error("data tree is null"); + return null; + } + if (dataTree instanceof SimpleNode) { + _logger.error("RPC XML was resolved as SimpleNode"); + return null; + } + return (CompositeNode) dataTree; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/scala/org/opendaylight/controller/sal/connector/remoterpc/Client.scala b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/scala/org/opendaylight/controller/sal/connector/remoterpc/Client.scala new file mode 100644 index 0000000000..63b68089d3 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/scala/org/opendaylight/controller/sal/connector/remoterpc/Client.scala @@ -0,0 +1,154 @@ +/* + * 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.connector.remoterpc + +import org.opendaylight.yangtools.yang.data.api.CompositeNode +import org.opendaylight.yangtools.yang.common.{RpcError, RpcResult, QName} +import org.opendaylight.controller.sal.core.api.RpcImplementation +import java.util +import java.util.{UUID, Collections} +import org.zeromq.ZMQ +import org.opendaylight.controller.sal.common.util.{RpcErrors, Rpcs} +import org.slf4j.LoggerFactory +import org.opendaylight.controller.sal.connector.remoterpc.dto.{MessageWrapper, RouteIdentifierImpl, Message} +import Message.MessageType +import java.util.concurrent._ +import java.lang.InterruptedException + + +/** + * An implementation of {@link RpcImplementation} that makes + * remote RPC calls + */ +class Client extends RemoteRpcClient { + + private val _logger = LoggerFactory.getLogger(this.getClass); + + val requestQueue = new LinkedBlockingQueue[MessageWrapper](100) + val pool: ExecutorService = Executors.newSingleThreadExecutor() + private val TIMEOUT = 5000 //in ms + var routingTableProvider: RoutingTableProvider = null + + + def getInstance = this + + + def setRoutingTableProvider(provider : RoutingTableProvider) = { + routingTableProvider = provider; + } + + def getSupportedRpcs: util.Set[QName] = { + Collections.emptySet() + } + + def invokeRpc(rpc: QName, input: CompositeNode): RpcResult[CompositeNode] = { + + val routeId = new RouteIdentifierImpl() + routeId.setType(rpc) + + //lookup address for the rpc request + val routingTable = routingTableProvider.getRoutingTable() + require( routingTable != null, "Routing table not found. Exiting" ) + + val addresses:util.Set[String] = routingTable.getRoutes(routeId.toString) + require(addresses != null, "Address not found for rpc " + rpc); + require(addresses.size() == 1) //its a global service. + + val address = addresses.iterator().next() + require(address != null, "Address is null") + + //create in-process "pair" socket and pass it to sender thread + //Sender replies on this when result is available + val inProcAddress = "inproc://" + UUID.randomUUID() + val receiver = Context.zmqContext.socket(ZMQ.PAIR) + receiver.bind(inProcAddress); + + val sender = Context.zmqContext.socket(ZMQ.PAIR) + sender.connect(inProcAddress) + + val requestMessage = new Message.MessageBuilder() + .`type`(MessageType.REQUEST) + //.sender("tcp://localhost:8081") + .recipient(address) + .route(routeId) + .payload(input) + .build() + + _logger.debug("Queuing up request and expecting response on [{}]", inProcAddress) + + val messageWrapper = new MessageWrapper(requestMessage, sender) + val errors = new util.ArrayList[RpcError] + + try { + process(messageWrapper) + val response = parseMessage(receiver) + + return Rpcs.getRpcResult( + true, response.getPayload.asInstanceOf[CompositeNode], Collections.emptySet()) + + } catch { + case e: Exception => { + errors.add(RpcErrors.getRpcError(null,null,null,null,e.getMessage,null,e.getCause)) + return Rpcs.getRpcResult(false, null, errors) + } + } finally { + receiver.close(); + sender.close(); + } + + } + + /** + * Block on socket for reply + * @param receiver + * @return + */ + private def parseMessage(receiver:ZMQ.Socket): Message = { + val bytes = receiver.recv() + return Message.deserialize(bytes).asInstanceOf[Message] + } + + def start() = { + pool.execute(new Sender) + } + + def process(msg: MessageWrapper) = { + _logger.debug("Processing message [{}]", msg) + val success = requestQueue.offer(msg, TIMEOUT, TimeUnit.MILLISECONDS) + + if (!success) throw new TimeoutException("Queue is full"); + + } + + def stop() = { + pool.shutdown() //intiate shutdown + _logger.debug("Client stopping...") + // Context.zmqContext.term(); + // _logger.debug("ZMQ context terminated") + + try { + + if (!pool.awaitTermination(10, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(10, TimeUnit.SECONDS)) + _logger.error("Client thread pool did not shut down"); + } + } catch { + case ie:InterruptedException => + // (Re-)Cancel if current thread also interrupted + pool.shutdownNow(); + // Preserve interrupt status + Thread.currentThread().interrupt(); + } + _logger.debug("Client stopped") + } + + def close() = { + stop(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/yang/odl-sal-dom-rpc-remote-cfg.yang b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/yang/odl-sal-dom-rpc-remote-cfg.yang new file mode 100644 index 0000000000..d20efe50c1 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/main/yang/odl-sal-dom-rpc-remote-cfg.yang @@ -0,0 +1,52 @@ +module odl-sal-dom-rpc-remote-cfg { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc"; + prefix "rpc-cluster"; + + import config { prefix config; revision-date 2013-04-05; } + import opendaylight-md-sal-dom {prefix dom;} + + description + "Service definition for Binding Aware MD-SAL."; + + revision "2013-10-28" { + description + "Initial revision"; + } + + identity remote-rpc-server { + base config:service-type; + config:java-class "org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcServer"; + } + + identity remote-rpc-client { + base config:service-type; + config:java-class "org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcClient"; + } + + identity remote-zeromq-rpc-server { + base config:module-type; + config:provided-service remote-rpc-server; + config:provided-service remote-rpc-client; + config:java-name-prefix ZeroMQServer; + } + + augment "/config:modules/config:module/config:configuration" { + case remote-zeromq-rpc-server { + when "/config:modules/config:module/config:type = 'remote-zeromq-rpc-server'"; + + container dom-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dom:dom-broker-osgi-registry; + } + } + } + + leaf port { + type uint16; + } + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/ClientTest.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/ClientTest.java new file mode 100644 index 0000000000..2e77537756 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/ClientTest.java @@ -0,0 +1,56 @@ +package org.opendaylight.controller.sal.connector.remoterpc; + +import junit.framework.Assert; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.sal.connector.remoterpc.dto.MessageWrapper; +import org.opendaylight.controller.sal.connector.remoterpc.dto.Message; + +import java.util.concurrent.TimeoutException; + +public class ClientTest { + + Client client; + + @Before + public void setup(){ + client = new Client(); + client.getRequestQueue().clear(); + } + + @Test + public void testStop() throws Exception { + + } + + @Test + public void testPool() throws Exception { + + } + + @Test + public void process_AddAMessage_ShouldAddToQueue() throws Exception { + client.process(getEmptyMessageWrapper()); + Assert.assertEquals(1, client.getRequestQueue().size()); + } + + /** + * Queue size is 100. Adding 101 message should time out in 2 sec + * if server does not process it + * @throws Exception + */ + @Test(expected = TimeoutException.class) + public void process_Add101Message_ShouldThrow() throws Exception { + for (int i=0;i<101;i++){ + client.process(getEmptyMessageWrapper()); + } + } + + @Test + public void testStart() throws Exception { + } + + private MessageWrapper getEmptyMessageWrapper(){ + return new MessageWrapper(new Message(), null); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/RouteIdentifierImplTest.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/RouteIdentifierImplTest.java new file mode 100644 index 0000000000..550d9ef125 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/RouteIdentifierImplTest.java @@ -0,0 +1,50 @@ +package org.opendaylight.controller.sal.connector.remoterpc; + +import org.codehaus.jackson.JsonParseException; +import org.junit.Assert; +import org.junit.Test; +import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl; +import org.opendaylight.yangtools.yang.common.QName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; + +public class RouteIdentifierImplTest { + + Logger _logger = LoggerFactory.getLogger(RouteIdentifierImplTest.class); + + private final URI namespace = URI.create("http://cisco.com/example"); + private final QName QNAME = new QName(namespace, "heartbeat"); + + @Test + public void testToString() throws Exception { + RouteIdentifierImpl rId = new RouteIdentifierImpl(); + rId.setType(QNAME); + + _logger.debug(rId.toString()); + + Assert.assertTrue(true); + + } + + @Test + public void testFromString() throws Exception { + RouteIdentifierImpl rId = new RouteIdentifierImpl(); + rId.setType(QNAME); + + _logger.debug("route: " + rId.fromString(rId.toString())); + + Assert.assertTrue(true); + } + + @Test(expected = JsonParseException.class) + public void testFromInvalidString() throws Exception { + String invalidInput = "aklhdgadfa;;;;;;;]]]]=]ag" ; + RouteIdentifierImpl rId = new RouteIdentifierImpl(); + rId.fromString(invalidInput); + + _logger.debug("" + rId); + Assert.assertTrue(true); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/RpcSocketTest.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/RpcSocketTest.java new file mode 100644 index 0000000000..e23a3ca5ef --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/RpcSocketTest.java @@ -0,0 +1,196 @@ +/* + * 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.connector.remoterpc; + +import junit.framework.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.opendaylight.controller.sal.connector.remoterpc.dto.Message; +import org.opendaylight.controller.sal.connector.remoterpc.dto.MessageWrapper; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.zeromq.ZMQ; + +import java.util.concurrent.TimeoutException; + +import static org.mockito.Mockito.doNothing; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(RpcSocket.class) +public class RpcSocketTest { + RpcSocket rpcSocket = new RpcSocket("tcp://localhost:5554", new ZMQ.Poller(1)); + RpcSocket spy = PowerMockito.spy(rpcSocket); + + @Test + public void testCreateSocket() throws Exception { + Assert.assertEquals("tcp://localhost:5554", spy.getAddress()); + Assert.assertEquals(ZMQ.REQ, spy.getSocket().getType()); + } + + @Test(expected = TimeoutException.class) + public void send_WhenQueueGetsFull_ShouldThrow() throws Exception { + + doNothing().when(spy).process(); + + //10 is queue size + for (int i=0;i<10;i++){ + spy.send(getEmptyMessageWrapper()); + } + + //sending 11th message should throw + spy.send(getEmptyMessageWrapper()); + } + + @Test + public void testHasTimedOut() throws Exception { + spy.send(getEmptyMessageWrapper()); + Assert.assertFalse(spy.hasTimedOut()); + Thread.sleep(1000); + Assert.assertFalse(spy.hasTimedOut()); + Thread.sleep(1000); + Assert.assertTrue(spy.hasTimedOut()); + } + + @Test + public void testProcess() throws Exception { + PowerMockito.doNothing().when(spy, "sendMessage"); + spy.send(getEmptyMessageWrapper()); + + //Next message should get queued + spy.send(getEmptyMessageWrapper()); + + //queue size should be 2 + Assert.assertEquals(2, spy.getQueueSize()); + + + spy.process(); + //sleep for 2 secs (timeout) + //message send would be retried + Thread.sleep(2000); + spy.process(); + Thread.sleep(2000); + spy.process(); + Thread.sleep(2000); + spy.process(); //retry fails, next message will get picked up + Assert.assertEquals(1, spy.getQueueSize()); + } + + @Test + public void testProcessStateTransitions() throws Exception { + PowerMockito.doNothing().when(spy, "sendMessage"); + Assert.assertTrue(spy.getState() instanceof RpcSocket.IdleSocketState); + spy.send(getEmptyMessageWrapper()); + Assert.assertEquals(1, spy.getQueueSize()); + Thread.sleep(200); + Assert.assertTrue(spy.getState() instanceof RpcSocket.BusySocketState); + Thread.sleep(1800); + + //1st timeout, 2nd try + spy.process(); + Thread.sleep(200); + Assert.assertTrue(spy.getState() instanceof RpcSocket.BusySocketState); + Thread.sleep(1800); + + //2nd timeout, 3rd try + spy.process(); + Thread.sleep(200); + Assert.assertTrue(spy.getState() instanceof RpcSocket.BusySocketState); + Thread.sleep(1800); + + //3rd timeout, no more tries => remove + spy.process(); + Thread.sleep(200); + Assert.assertTrue(spy.getState() instanceof RpcSocket.IdleSocketState); + Assert.assertEquals(0, spy.getQueueSize()); + } + + @Test + public void testParseMessage() throws Exception { + // Write an integration test for parseMessage + } + + @Test + public void testRecycleSocket() throws Exception { + // This will need to be updated in the future...for now, recycleSocket() calls close() + Assert.assertTrue(spy.getSocket().base().check_tag()); + spy.close(); + Assert.assertEquals(10, spy.getSocket().getLinger()); + Assert.assertFalse(spy.getSocket().base().check_tag()); + } + + @Test + public void testClose() throws Exception { + Assert.assertTrue(spy.getSocket().base().check_tag()); + spy.close(); + Assert.assertEquals(10, spy.getSocket().getLinger()); + Assert.assertFalse(spy.getSocket().base().check_tag()); + } + + @Test + public void testReceive() throws Exception { + PowerMockito.doReturn(null).when(spy, "parseMessage"); + PowerMockito.doNothing().when(spy, "process"); + spy.send(getEmptyMessageWrapper()); + + //There should be 1 message waiting in the queue + Assert.assertEquals(1, spy.getQueueSize()); + + spy.receive(); + //This should complete message processing + //The message should be removed from the queue + Assert.assertEquals(0, spy.getQueueSize()); + Assert.assertEquals(RpcSocket.NUM_RETRIES, spy.getRetriesLeft()); + + } + + @Test + public void testReceiveStateTransitions() throws Exception { + PowerMockito.doReturn(null).when(spy, "parseMessage"); + Assert.assertTrue(spy.getState() instanceof RpcSocket.IdleSocketState); + spy.send(getEmptyMessageWrapper()); + + //There should be 1 message waiting in the queue + Assert.assertEquals(1, spy.getQueueSize()); + Assert.assertTrue(spy.getState() instanceof RpcSocket.BusySocketState); + + spy.receive(); + //This should complete message processing + //The message should be removed from the queue + Assert.assertEquals(0, spy.getQueueSize()); + Assert.assertTrue(spy.getState() instanceof RpcSocket.IdleSocketState); + } + + private MessageWrapper getEmptyMessageWrapper(){ + return new MessageWrapper(new Message(), null); + } + + @Test + public void testProcessReceiveSequence() throws Exception { + PowerMockito.doNothing().when(spy, "sendMessage"); + PowerMockito.doReturn(null).when(spy, "parseMessage"); + Assert.assertTrue(spy.getState() instanceof RpcSocket.IdleSocketState); + spy.send(getEmptyMessageWrapper()); + spy.send(getEmptyMessageWrapper()); + Assert.assertEquals(2, spy.getQueueSize()); + Assert.assertTrue(spy.getState() instanceof RpcSocket.BusySocketState); + + + Thread.sleep(2000); + Assert.assertTrue(spy.getState() instanceof RpcSocket.BusySocketState); + spy.receive(); + Assert.assertTrue(spy.getState() instanceof RpcSocket.IdleSocketState); + Assert.assertEquals(1, spy.getQueueSize()); + + spy.process(); + Assert.assertTrue(spy.getState() instanceof RpcSocket.BusySocketState); + spy.receive(); + Assert.assertTrue(spy.getState() instanceof RpcSocket.IdleSocketState); + Assert.assertEquals(0, spy.getQueueSize()); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/SerilizationTest.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/SerilizationTest.java new file mode 100644 index 0000000000..36a4acddca --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/SerilizationTest.java @@ -0,0 +1,79 @@ +package org.opendaylight.controller.sal.connector.remoterpc; + +import org.junit.Test; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.impl.NodeUtils; +import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; + +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.StringWriter; + +public class SerilizationTest { + + private static final Logger _logger = LoggerFactory.getLogger(SerilizationTest.class); + + public void fromXml() { + } + + @Test + public void toXml() throws FileNotFoundException { + + InputStream xmlStream = SerilizationTest.class.getResourceAsStream("/FourSimpleChildren.xml"); + StringWriter writer = new StringWriter(); + + CompositeNode data = loadCompositeNode(xmlStream); + Document domTree = NodeUtils.buildShadowDomTree(data); + try { + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + //transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + //transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + //transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + //transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + transformer.transform(new DOMSource(domTree), new StreamResult(writer)); + } catch (TransformerException e) { + _logger.error("Error during translation of Document to OutputStream", e); + } + + _logger.info("Parsed xml [{}]", writer.toString()); + } + + //Note to self: Stolen from TestUtils + ///Users/alefan/odl/controller4/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java + // Figure out how to include TestUtils through pom ...was getting errors + private CompositeNode loadCompositeNode(InputStream xmlInputStream) throws FileNotFoundException { + if (xmlInputStream == null) { + throw new IllegalArgumentException(); + } + Node dataTree; + try { + dataTree = XmlTreeBuilder.buildDataTree(xmlInputStream); + } catch (XMLStreamException e) { + _logger.error("Error during building data tree from XML", e); + return null; + } + if (dataTree == null) { + _logger.error("data tree is null"); + return null; + } + if (dataTree instanceof SimpleNode) { + _logger.error("RPC XML was resolved as SimpleNode"); + return null; + } + return (CompositeNode) dataTree; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/SocketManagerTest.java b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/SocketManagerTest.java new file mode 100644 index 0000000000..130b30d5f5 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/java/org/opendaylight/controller/sal/connector/remoterpc/SocketManagerTest.java @@ -0,0 +1,118 @@ +/* + * 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.connector.remoterpc; + +import com.google.common.base.Optional; +import junit.framework.Assert; +import org.junit.After; +import org.junit.Before; +import org.zeromq.ZMQ; +import org.opendaylight.controller.sal.connector.remoterpc.SocketManager; +import org.opendaylight.controller.sal.connector.remoterpc.RpcSocket; +import org.opendaylight.controller.sal.connector.remoterpc.Context; +import org.junit.Test; + +public class SocketManagerTest { + + SocketManager manager; + + @Before + public void setup(){ + manager = new SocketManager(); + } + + @After + public void tearDown() throws Exception { + manager.close(); + } + + @Test + public void getManagedSockets_When2NewAdded_ShouldContain2() throws Exception { + + //Prepare data + manager.getManagedSocket("tcp://localhost:5554"); + manager.getManagedSocket("tcp://localhost:5555"); + + Assert.assertTrue( 2 == manager.getManagedSockets().size()); + } + + @Test + public void getManagedSockets_When2NewAddedAnd1Existing_ShouldContain2() throws Exception { + + //Prepare data + manager.getManagedSocket("tcp://localhost:5554"); + manager.getManagedSocket("tcp://localhost:5555"); + manager.getManagedSocket("tcp://localhost:5554"); //ask for the first one + + Assert.assertTrue( 2 == manager.getManagedSockets().size()); + } + + @Test + public void getManagedSocket_WhenPassedAValidAddress_ShouldReturnARpcSocket() throws Exception { + String testAddress = "tcp://localhost:5554"; + RpcSocket rpcSocket = manager.getManagedSocket(testAddress); + Assert.assertEquals(testAddress, rpcSocket.getAddress()); + } + + @Test(expected = IllegalArgumentException.class) + public void getManagedSocket_WhenPassedInvalidHostAddress_ShouldThrow() throws Exception { + String testAddress = "tcp://nonexistenthost:5554"; + RpcSocket rpcSocket = manager.getManagedSocket(testAddress); + } + + @Test(expected = IllegalArgumentException.class) + public void getManagedSocket_WhenPassedInvalidAddress_ShouldThrow() throws Exception { + String testAddress = "xxx"; + RpcSocket rpcSocket = manager.getManagedSocket(testAddress); + } + + @Test + public void getManagedSocket_WhenPassedAValidZmqSocket_ShouldReturnARpcSocket() throws Exception { + //Prepare data + String firstAddress = "tcp://localhost:5554"; + RpcSocket firstRpcSocket = manager.getManagedSocket(firstAddress); + ZMQ.Socket firstZmqSocket = firstRpcSocket.getSocket(); + + String secondAddress = "tcp://localhost:5555"; + RpcSocket secondRpcSocket = manager.getManagedSocket(secondAddress); + ZMQ.Socket secondZmqSocket = secondRpcSocket.getSocket(); + + Assert.assertEquals(firstRpcSocket, manager.getManagedSocketFor(firstZmqSocket).get()); + Assert.assertEquals(secondRpcSocket, manager.getManagedSocketFor(secondZmqSocket).get()); + } + + @Test + public void getManagedSocket_WhenPassedNonManagedZmqSocket_ShouldReturnNone() throws Exception { + ZMQ.Socket nonManagedSocket = Context.getInstance().getZmqContext().socket(ZMQ.REQ); + nonManagedSocket.connect("tcp://localhost:5000"); + + //Prepare data + String firstAddress = "tcp://localhost:5554"; + RpcSocket firstRpcSocket = manager.getManagedSocket(firstAddress); + ZMQ.Socket firstZmqSocket = firstRpcSocket.getSocket(); + + Assert.assertSame(Optional.absent(), manager.getManagedSocketFor(nonManagedSocket) ); + Assert.assertSame(Optional.absent(), manager.getManagedSocketFor(null) ); + } + + @Test + public void stop_WhenCalled_ShouldEmptyManagedSockets() throws Exception { + manager.getManagedSocket("tcp://localhost:5554"); + manager.getManagedSocket("tcp://localhost:5555"); + Assert.assertTrue( 2 == manager.getManagedSockets().size()); + + manager.close(); + Assert.assertTrue( 0 == manager.getManagedSockets().size()); + } + + @Test + public void poller_WhenCalled_ShouldReturnAnInstanceOfPoller() throws Exception { + Assert.assertTrue (manager.getPoller() instanceof ZMQ.Poller); + } + +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/resources/FourSimpleChildren.xml b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/resources/FourSimpleChildren.xml new file mode 100644 index 0000000000..5ac991b120 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/implementation/src/test/resources/FourSimpleChildren.xml @@ -0,0 +1,6 @@ + + eth0 + ethernetCsmacd + false + some interface + diff --git a/opendaylight/md-sal/test/zeromq-test-consumer/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/pom.xml similarity index 87% rename from opendaylight/md-sal/test/zeromq-test-consumer/pom.xml rename to opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/pom.xml index 7c6bc21b46..fa7b73be0e 100644 --- a/opendaylight/md-sal/test/zeromq-test-consumer/pom.xml +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/pom.xml @@ -2,11 +2,11 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - sal-test-parent + sal-remoterpc-connector-test-parent org.opendaylight.controller.tests 1.0-SNAPSHOT - zeromq-test-consumer + sal-remoterpc-connector-test-consumer bundle scm:git:ssh://git.opendaylight.org:29418/controller.git @@ -23,11 +23,6 @@ org.opendaylight.controller.sample.zeromq.consumer.ExampleConsumer - - org.opendaylight.controller.sal.core.api, - org.opendaylight.yangtools.yang.common;version="[0.5,1)", - org.opendaylight.yangtools.yang.data.api, - @@ -75,6 +70,11 @@ org.opendaylight.yangtools yang-data-api + + org.opendaylight.yangtools + yang-data-impl + 0.5.9-SNAPSHOT + org.opendaylight.controller diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/java/org/opendaylight/controller/sample/zeromq/consumer/ExampleConsumer.java b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/java/org/opendaylight/controller/sample/zeromq/consumer/ExampleConsumer.java new file mode 100644 index 0000000000..87078ea712 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/java/org/opendaylight/controller/sample/zeromq/consumer/ExampleConsumer.java @@ -0,0 +1,122 @@ +package org.opendaylight.controller.sample.zeromq.consumer; + +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.net.URI; +import java.util.Hashtable; +import java.util.concurrent.*; + +import org.opendaylight.controller.sal.core.api.AbstractConsumer; +import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder; +import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; + +import javax.xml.stream.XMLStreamException; + +public class ExampleConsumer extends AbstractConsumer { + + private final URI namespace = URI.create("http://cisco.com/example"); + private final QName QNAME = new QName(namespace, "heartbeat"); + + private ConsumerSession session; + + private ServiceRegistration thisReg; + private Logger _logger = LoggerFactory.getLogger(ExampleConsumer.class); + + @Override + public void onSessionInitiated(ConsumerSession session) { + this.session = session; + } + + public RpcResult invokeRpc(QName qname, CompositeNode input) { + _logger.info("Invoking RPC:[{}] with Input:[{}]", qname.getLocalName(), input); + RpcResult result = null; + Future> future = ExampleConsumer.this.session.rpc(qname, input); + try { + result = future.get(); + } catch (Exception e) { + e.printStackTrace(); + } + _logger.info("Returning Result:[{}]", result); + return result; + } + + @Override + protected void startImpl(BundleContext context){ + thisReg = context.registerService(ExampleConsumer.class, this, new Hashtable()); + } + @Override + protected void stopImpl(BundleContext context) { + super.stopImpl(context); + thisReg.unregister(); + } + + public CompositeNode getValidCompositeNodeWithOneSimpleChild() throws FileNotFoundException { + InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/OneSimpleChild.xml"); + return loadCompositeNode(xmlStream); + } + + public CompositeNode getValidCompositeNodeWithTwoSimpleChildren() throws FileNotFoundException { + InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/TwoSimpleChildren.xml"); + return loadCompositeNode(xmlStream); + } + + public CompositeNode getValidCompositeNodeWithFourSimpleChildren() throws FileNotFoundException { + InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/FourSimpleChildren.xml"); + return loadCompositeNode(xmlStream); + } + + public CompositeNode getValidCompositeNodeWithOneSimpleOneCompositeChild() throws FileNotFoundException { + InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/OneSimpleOneCompositeChild.xml"); + return loadCompositeNode(xmlStream); + } + + public CompositeNode getValidCompositeNodeWithTwoCompositeChildren() throws FileNotFoundException { + InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/TwoCompositeChildren.xml"); + return loadCompositeNode(xmlStream); + } + + public CompositeNode getInvalidCompositeNodeSimpleChild() throws FileNotFoundException { + InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/InvalidSimpleChild.xml"); + return loadCompositeNode(xmlStream); + } + + public CompositeNode getInvalidCompositeNodeCompositeChild() throws FileNotFoundException { + InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/InvalidCompositeChild.xml"); + return loadCompositeNode(xmlStream); + } + + //Note to self: Stolen from TestUtils + ///Users/alefan/odl/controller4/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java + // Figure out how to include TestUtils through pom ...was getting errors + private CompositeNode loadCompositeNode(InputStream xmlInputStream) throws FileNotFoundException { + if (xmlInputStream == null) { + throw new IllegalArgumentException(); + } + Node dataTree; + try { + dataTree = XmlTreeBuilder.buildDataTree(xmlInputStream); + } catch (XMLStreamException e) { + _logger.error("Error during building data tree from XML", e); + return null; + } + if (dataTree == null) { + _logger.error("data tree is null"); + return null; + } + if (dataTree instanceof SimpleNode) { + _logger.error("RPC XML was resolved as SimpleNode"); + return null; + } + return (CompositeNode) dataTree; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/FourSimpleChildren.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/FourSimpleChildren.xml new file mode 100644 index 0000000000..5ac991b120 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/FourSimpleChildren.xml @@ -0,0 +1,6 @@ + + eth0 + ethernetCsmacd + false + some interface + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/InvalidCompositeChild.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/InvalidCompositeChild.xml new file mode 100644 index 0000000000..3979d02ccf --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/InvalidCompositeChild.xml @@ -0,0 +1,14 @@ + + + eth1 + ethernet + false + some interface + + + error + ethernet + true + some interface + + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/InvalidSimpleChild.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/InvalidSimpleChild.xml new file mode 100644 index 0000000000..6082d72a71 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/InvalidSimpleChild.xml @@ -0,0 +1,3 @@ + + error + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/OneSimpleChild.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/OneSimpleChild.xml new file mode 100644 index 0000000000..f431b0453d --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/OneSimpleChild.xml @@ -0,0 +1,3 @@ + + eth0 + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/OneSimpleOneCompositeChild.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/OneSimpleOneCompositeChild.xml new file mode 100644 index 0000000000..bca7682ee7 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/OneSimpleOneCompositeChild.xml @@ -0,0 +1,9 @@ + + eth0 + + eth1 + ethernetCsmacd + false + some interface + + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/TwoCompositeChildren.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/TwoCompositeChildren.xml new file mode 100644 index 0000000000..c49407e4c0 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/TwoCompositeChildren.xml @@ -0,0 +1,14 @@ + + + eth1 + ethernet + false + some interface + + + eth2 + ethernet + true + some interface + + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/TwoSimpleChildren.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/TwoSimpleChildren.xml new file mode 100644 index 0000000000..5f4729c99d --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/consumer-service/src/main/resources/TwoSimpleChildren.xml @@ -0,0 +1,4 @@ + + eth0 + ethernetCsmacd + \ No newline at end of file diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/pom.xml new file mode 100644 index 0000000000..5bfbcba5f8 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/pom.xml @@ -0,0 +1,26 @@ + + 4.0.0 + + org.opendaylight.controller + sal-parent + 1.0-SNAPSHOT + ../.. + + pom + org.opendaylight.controller.tests + sal-remoterpc-connector-test-parent + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL + + + + consumer-service + provider-service + test-it + test-nb + + + diff --git a/opendaylight/md-sal/test/zeromq-test-provider/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/provider-service/pom.xml similarity index 89% rename from opendaylight/md-sal/test/zeromq-test-provider/pom.xml rename to opendaylight/md-sal/sal-remoterpc-connector/integrationtest/provider-service/pom.xml index 10e15aa917..a13a5aeba0 100644 --- a/opendaylight/md-sal/test/zeromq-test-provider/pom.xml +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/provider-service/pom.xml @@ -2,11 +2,11 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - sal-test-parent + sal-remoterpc-connector-test-parent org.opendaylight.controller.tests 1.0-SNAPSHOT - zeromq-test-provider + sal-remoterpc-connector-test-provider bundle scm:git:ssh://git.opendaylight.org:29418/controller.git @@ -70,7 +70,10 @@ org.opendaylight.yangtools yang-data-api - + + org.opendaylight.yangtools + yang-data-impl + org.opendaylight.controller sal-common-util @@ -78,7 +81,7 @@ org.opendaylight.controller - sal-zeromq-connector + sal-remoterpc-connector 1.0-SNAPSHOT diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/provider-service/src/main/java/org/opendaylight/controller/sample/zeromq/provider/ExampleProvider.java b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/provider-service/src/main/java/org/opendaylight/controller/sample/zeromq/provider/ExampleProvider.java new file mode 100644 index 0000000000..6c294dddcc --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/provider-service/src/main/java/org/opendaylight/controller/sample/zeromq/provider/ExampleProvider.java @@ -0,0 +1,120 @@ +package org.opendaylight.controller.sample.zeromq.provider; + +import org.opendaylight.controller.sal.common.util.RpcErrors; +import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.sal.connector.remoterpc.dto.CompositeNodeImpl; +import org.opendaylight.controller.sal.core.api.AbstractProvider; +import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; +import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; +import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; +import java.util.*; + +public class ExampleProvider extends AbstractProvider implements RpcImplementation { + + private final URI namespace = URI.create("http://cisco.com/example"); + private final QName QNAME = new QName(namespace, "heartbeat"); + private RpcRegistration reg; + + private ServiceRegistration thisReg; + + private ProviderSession session; + private Logger _logger = LoggerFactory.getLogger(ExampleProvider.class); + + @Override + public void onSessionInitiated(ProviderSession session) { + this.session = session; + } + + @Override + public Set getSupportedRpcs() { + Set supportedRpcs = new HashSet(); + supportedRpcs.add(QNAME); + return supportedRpcs; + } + + @Override + public RpcResult invokeRpc(final QName rpc, CompositeNode input) { + boolean success = false; + CompositeNode output = null; + Collection errors = new ArrayList<>(); + + // Only handle supported RPC calls + if (getSupportedRpcs().contains(rpc)) { + if (input == null) { + errors.add(RpcErrors.getRpcError("app", "tag", "info", RpcError.ErrorSeverity.WARNING, "message:null input", RpcError.ErrorType.RPC, null)); + } + else { + if (isErroneousInput(input)) { + errors.add(RpcErrors.getRpcError("app", "tag", "info", RpcError.ErrorSeverity.ERROR, "message:error", RpcError.ErrorType.RPC, null)); + } + else { + success = true; + output = addSuccessNode(input); + } + } + } + return Rpcs.getRpcResult(success, output, errors); + } + + // Examines input -- dives into CompositeNodes and finds any value equal to "error" + private boolean isErroneousInput(CompositeNode input) { + for (Node n : input.getChildren()) { + if (n instanceof CompositeNode) { + if (isErroneousInput((CompositeNode)n)) { + return true; + } + } + else { //SimpleNode + if ((input.getChildren().get(0).getValue()).equals("error")) { + return true; + } + } + } + return false; + } + + // Adds a child SimpleNode containing the value "success" to the input CompositeNode + private CompositeNode addSuccessNode(CompositeNode input) { + List> list = new ArrayList>(input.getChildren()); + SimpleNodeTOImpl simpleNode = new SimpleNodeTOImpl(QNAME, input, "success"); + list.add(simpleNode); + return new CompositeNodeTOImpl(QNAME, null, list); + } + + @Override + protected void startImpl(BundleContext context) { + thisReg = context.registerService(ExampleProvider.class, this, new Hashtable()); + } + + @Override + protected void stopImpl(BundleContext context) { + if (reg != null) { + try { + reg.close(); + thisReg.unregister(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + public void announce(QName name) { + _logger.debug("Announcing [{}]\n\n\n", name); + reg = this.session.addRpcImplementation(name, this); + } + +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/pom.xml new file mode 100644 index 0000000000..4305a283e2 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/pom.xml @@ -0,0 +1,536 @@ + + 4.0.0 + + sal-remoterpc-connector-test-parent + org.opendaylight.controller.tests + 1.0-SNAPSHOT + + sal-remoterpc-connector-test-it + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL + + + + 3.0.0 + 1.5.0 + 0.2.3-SNAPSHOT + 0.2.3-SNAPSHOT + + + + + + commons-codec + commons-codec + 1.7 + + + + + + + + + org.ops4j.pax.exam + maven-paxexam-plugin + 1.2.4 + + + generate-config + + generate-depends-file + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.ops4j.pax.exam + + + maven-paxexam-plugin + + + [1.2.4,) + + + + generate-depends-file + + + + + + + + + + + + + + + + + + org.opendaylight.yangtools.thirdparty + xtend-lib-osgi + 2.4.3 + + + org.opendaylight.controller.tests + sal-remoterpc-connector-test-provider + 1.0-SNAPSHOT + + + org.opendaylight.controller.tests + sal-remoterpc-connector-test-consumer + 1.0-SNAPSHOT + + + org.opendaylight.controller + sal-broker-impl + 1.0-SNAPSHOT + + + org.ops4j.pax.exam + pax-exam-container-native + ${exam.version} + test + + + org.ops4j.pax.exam + pax-exam-junit4 + ${exam.version} + test + + + org.ops4j.pax.exam + pax-exam-link-mvn + ${exam.version} + test + + + org.ops4j.pax.url + pax-url-aether + 1.5.2 + test + + + equinoxSDK381 + org.eclipse.osgi + 3.8.1.v20120830-144521 + test + + + org.slf4j + log4j-over-slf4j + 1.7.2 + + + ch.qos.logback + logback-core + 1.0.9 + + + ch.qos.logback + logback-classic + 1.0.9 + + + org.opendaylight.controller + sal-binding-api + 1.0-SNAPSHOT + + + org.opendaylight.controller + sal-common-util + 1.0-SNAPSHOT + + + org.opendaylight.controller + sal-core-api + 1.0-SNAPSHOT + + + org.opendaylight.controller + sal-remoterpc-connector + 1.0-SNAPSHOT + + + + org.opendaylight.controller + containermanager + 0.5.1-SNAPSHOT + + + org.osgi + org.osgi.compendium + + + commons-io + commons-io + + + + + + org.opendaylight.yangtools + yang-binding + + + org.opendaylight.yangtools + yang-common + + + org.opendaylight.yangtools + yang-data-api + + + + org.opendaylight.yangtools + yang-parser-impl + 0.5.9-SNAPSHOT + + + org.opendaylight.controller + sal-common-util + 1.0-SNAPSHOT + + + org.opendaylight.yangtools.thirdparty + antlr4-runtime-osgi-nohead + 4.0 + + + + + org.opendaylight.controller + zeromq-routingtable.implementation + 0.4.1-SNAPSHOT + + + org.opendaylight.controller + clustering.services + 0.4.1-SNAPSHOT + + + org.opendaylight.controller + sal + 0.5.1-SNAPSHOT + + + org.osgi + org.osgi.compendium + + + + + org.opendaylight.controller + sal.implementation + 0.4.0-SNAPSHOT + + + commons-io + commons-io + + + + + org.opendaylight.controller + containermanager + 0.5.0-SNAPSHOT + + + org.osgi + org.osgi.compendium + + + commons-io + commons-io + + + + + org.opendaylight.controller + containermanager.it.implementation + 0.5.0-SNAPSHOT + + + commons-io + commons-io + + + + + org.opendaylight.controller + clustering.stub + 0.4.0-SNAPSHOT + + + commons-io + commons-io + + + + + + org.apache.felix + org.apache.felix.dependencymanager.shell + 3.0.1 + + + org.osgi + org.osgi.compendium + + + + + eclipselink + javax.resource + 1.5.0.v200906010428 + + + com.google.guava + guava + + + org.opendaylight.controller + sal + 0.5.1-SNAPSHOT + + + org.opendaylight.controller + ietf-netconf-monitoring + ${netconf.version} + + + org.opendaylight.yangtools + yang-binding + + + org.opendaylight.yangtools.model + yang-ext + 2013.09.07.1 + + + org.opendaylight.yangtools.model + opendaylight-l2-types + 2013.08.27.1 + + + org.opendaylight.controller + sal-binding-it + 1.0-SNAPSHOT + + + org.opendaylight.controller + sal-binding-config + 1.0-SNAPSHOT + + + org.opendaylight.controller + sal-binding-broker-impl + 1.0-SNAPSHOT + + + org.opendaylight.controller + sal-broker-impl + 1.0-SNAPSHOT + + + + org.opendaylight.controller.model + model-inventory + 1.0-SNAPSHOT + + + org.opendaylight.yangtools + yang-common + + + org.opendaylight.controller + sal-connector-api + 1.0-SNAPSHOT + + + org.opendaylight.controller + sal-common-util + 1.0-SNAPSHOT + + + + org.opendaylight.controller + clustering.services + 0.4.1-SNAPSHOT + + + + equinoxSDK381 + org.eclipse.osgi + 3.8.1.v20120830-144521 + + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.2 + + + org.codehaus.jackson + jackson-core-asl + 1.9.2 + + + org.zeromq + jeromq + 0.3.1 + + + org.opendaylight.yangtools.thirdparty + xtend-lib-osgi + 2.4.3 + test + + + org.opendaylight.controller + sal-binding-broker-impl + 1.0-SNAPSHOT + provided + + + org.ops4j.pax.exam + pax-exam-container-native + ${exam.version} + test + + + org.ops4j.pax.exam + pax-exam-junit4 + ${exam.version} + test + + + org.opendaylight.controller + config-netconf-connector + ${netconf.version} + test + + + org.opendaylight.controller + yang-store-impl + ${config.version} + + + org.opendaylight.controller + logback-config + ${config.version} + + + org.opendaylight.controller + config-persister-impl + ${config.version} + + + org.opendaylight.controller + config-persister-file-adapter + ${config.version} + + + org.opendaylight.controller + netconf-impl + ${netconf.version} + + + org.opendaylight.controller + netconf-client + ${netconf.version} + + + org.ops4j.pax.exam + pax-exam + ${exam.version} + + compile + + + org.ops4j.pax.exam + pax-exam-link-mvn + ${exam.version} + test + + + equinoxSDK381 + org.eclipse.osgi + 3.8.1.v20120830-144521 + test + + + org.slf4j + log4j-over-slf4j + 1.7.2 + + + ch.qos.logback + logback-core + 1.0.9 + + + ch.qos.logback + logback-classic + 1.0.9 + + + org.mockito + mockito-all + test + + + org.opendaylight.controller.model + model-flow-service + 1.0-SNAPSHOT + provided + + + org.opendaylight.controller + config-manager + 0.2.3-SNAPSHOT + + + commons-io + commons-io + + + + + org.opendaylight.controller.model + model-flow-management + 1.0-SNAPSHOT + provided + + + org.opendaylight.yangtools.thirdparty + antlr4-runtime-osgi-nohead + 4.0 + + + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/src/test/java/org/opendaylight/controller/sample/zeromq/test/it/RouterTest.java b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/src/test/java/org/opendaylight/controller/sample/zeromq/test/it/RouterTest.java new file mode 100644 index 0000000000..62c094d7a6 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/src/test/java/org/opendaylight/controller/sample/zeromq/test/it/RouterTest.java @@ -0,0 +1,456 @@ +/* + * 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.sample.zeromq.test.it; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.opendaylight.controller.sal.connector.remoterpc.Client; +import org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcClient; +import org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcServer; +import org.opendaylight.controller.sal.connector.remoterpc.ServerImpl; +import org.opendaylight.controller.sal.connector.remoterpc.dto.Message; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sample.zeromq.provider.ExampleProvider; +import org.opendaylight.controller.sample.zeromq.consumer.ExampleConsumer; +import org.opendaylight.controller.test.sal.binding.it.TestHelper; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.util.Filter; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleException; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zeromq.ZMQ; + +import javax.inject.Inject; + +import java.io.IOException; +import java.net.URI; +import java.util.Hashtable; + +import static org.opendaylight.controller.test.sal.binding.it.TestHelper.baseModelBundles; +import static org.opendaylight.controller.test.sal.binding.it.TestHelper.bindingAwareSalBundles; +import static org.opendaylight.controller.test.sal.binding.it.TestHelper.configMinumumBundles; +import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles; +import static org.ops4j.pax.exam.CoreOptions.*; + +@RunWith(PaxExam.class) +public class RouterTest { + + private Logger _logger = LoggerFactory.getLogger(RouterTest.class); + + public static final String ODL = "org.opendaylight.controller"; + public static final String YANG = "org.opendaylight.yangtools"; + public static final String SAMPLE = "org.opendaylight.controller.tests"; + private final URI namespace = URI.create("http://cisco.com/example"); + private final QName QNAME = new QName(namespace, "heartbeat"); + + + @Inject + org.osgi.framework.BundleContext ctx; + + @Inject + @Filter(timeout=60*1000) + Broker broker; + + private ZMQ.Context zmqCtx = ZMQ.context(1); + //private Server router; + //private ExampleProvider provider; + + //@Test + public void testInvokeRpc() throws Exception{ + //Thread.sleep(1000); + //Send announcement + ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class); + Assert.assertNotNull(providerRef); + + ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef); + Assert.assertNotNull(provider); + + ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class); + Assert.assertNotNull(consumerRef); + ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef); + Assert.assertNotNull(consumer); + + + _logger.debug("Provider sends announcement [{}]", "heartbeat"); + provider.announce(QNAME); + ServiceReference routerRef = ctx.getServiceReference(Client.class); + Client router = (Client) ctx.getService(routerRef); + _logger.debug("Found router[{}]", router); + _logger.debug("Invoking RPC [{}]", QNAME); + for (int i = 0; i < 3; i++) { + RpcResult result = router.invokeRpc(QNAME, consumer.getValidCompositeNodeWithOneSimpleChild()); + _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors()); + Assert.assertNotNull(result); + } + } + + @Test + public void testInvokeRpcWithValidSimpleNode() throws Exception{ + //Thread.sleep(1500); + + ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class); + Assert.assertNotNull(providerRef); + ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef); + Assert.assertNotNull(provider); + ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class); + Assert.assertNotNull(consumerRef); + ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef); + Assert.assertNotNull(consumer); + + // Provider sends announcement + _logger.debug("Provider sends announcement [{}]", "heartbeat"); + provider.announce(QNAME); + // Consumer invokes RPC + _logger.debug("Invoking RPC [{}]", QNAME); + CompositeNode input = consumer.getValidCompositeNodeWithOneSimpleChild(); + for (int i = 0; i < 3; i++) { + RpcResult result = consumer.invokeRpc(QNAME, input); + Assert.assertNotNull(result); + _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors()); + Assert.assertTrue(result.isSuccessful()); + Assert.assertNotNull(result.getResult()); + Assert.assertEquals(0, result.getErrors().size()); + Assert.assertEquals(input.getChildren().size()+1, result.getResult().getChildren().size()); + } + } + + @Test + public void testInvokeRpcWithValidSimpleNodes() throws Exception{ + //Thread.sleep(1500); + + ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class); + Assert.assertNotNull(providerRef); + ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef); + Assert.assertNotNull(provider); + ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class); + Assert.assertNotNull(consumerRef); + ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef); + Assert.assertNotNull(consumer); + + // Provider sends announcement + _logger.debug("Provider sends announcement [{}]", "heartbeat"); + provider.announce(QNAME); + // Consumer invokes RPC + _logger.debug("Invoking RPC [{}]", QNAME); + CompositeNode input = consumer.getValidCompositeNodeWithFourSimpleChildren(); + for (int i = 0; i < 3; i++) { + RpcResult result = consumer.invokeRpc(QNAME, input); + Assert.assertNotNull(result); + _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors()); + Assert.assertTrue(result.isSuccessful()); + Assert.assertNotNull(result.getResult()); + Assert.assertEquals(0, result.getErrors().size()); + Assert.assertEquals(input.getChildren().size()+1, result.getResult().getChildren().size()); + } + } + + @Test + public void testInvokeRpcWithValidCompositeNode() throws Exception{ + //Thread.sleep(1500); + + ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class); + Assert.assertNotNull(providerRef); + ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef); + Assert.assertNotNull(provider); + ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class); + Assert.assertNotNull(consumerRef); + ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef); + Assert.assertNotNull(consumer); + + // Provider sends announcement + _logger.debug("Provider sends announcement [{}]", "heartbeat"); + provider.announce(QNAME); + // Consumer invokes RPC + _logger.debug("Invoking RPC [{}]", QNAME); + CompositeNode input = consumer.getValidCompositeNodeWithTwoCompositeChildren(); + for (int i = 0; i < 3; i++) { + RpcResult result = consumer.invokeRpc(QNAME, input); + Assert.assertNotNull(result); + _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors()); + Assert.assertTrue(result.isSuccessful()); + Assert.assertNotNull(result.getResult()); + Assert.assertEquals(0, result.getErrors().size()); + Assert.assertEquals(input.getChildren().size()+1, result.getResult().getChildren().size()); + } + } + + @Test + public void testInvokeRpcWithNullInput() throws Exception{ + //Thread.sleep(1500); + + ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class); + Assert.assertNotNull(providerRef); + ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef); + Assert.assertNotNull(provider); + ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class); + Assert.assertNotNull(consumerRef); + ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef); + Assert.assertNotNull(consumer); + + // Provider sends announcement + _logger.debug("Provider sends announcement [{}]", QNAME.getLocalName()); + provider.announce(QNAME); + // Consumer invokes RPC + _logger.debug("Invoking RPC [{}]", QNAME); + for (int i = 0; i < 3; i++) { + RpcResult result = consumer.invokeRpc(QNAME, null); + Assert.assertNotNull(result); + _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors()); + Assert.assertFalse(result.isSuccessful()); + Assert.assertNull(result.getResult()); + Assert.assertEquals(1, result.getErrors().size()); + Assert.assertEquals(RpcError.ErrorSeverity.WARNING, ((RpcError)result.getErrors().toArray()[0]).getSeverity()); + } + } + + @Test + public void testInvokeRpcWithInvalidSimpleNode() throws Exception{ + //Thread.sleep(1500); + + ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class); + Assert.assertNotNull(providerRef); + ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef); + Assert.assertNotNull(provider); + ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class); + Assert.assertNotNull(consumerRef); + ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef); + Assert.assertNotNull(consumer); + + // Provider sends announcement + _logger.debug("Provider sends announcement [{}]", QNAME.getLocalName()); + provider.announce(QNAME); + // Consumer invokes RPC + _logger.debug("Invoking RPC [{}]", QNAME); + CompositeNode input = consumer.getInvalidCompositeNodeSimpleChild(); + for (int i = 0; i < 3; i++) { + RpcResult result = consumer.invokeRpc(QNAME, input); + Assert.assertNotNull(result); + _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors()); + Assert.assertFalse(result.isSuccessful()); + Assert.assertNull(result.getResult()); + Assert.assertEquals(1, result.getErrors().size()); + Assert.assertEquals(RpcError.ErrorSeverity.ERROR, ((RpcError)result.getErrors().toArray()[0]).getSeverity()); + } + } + + @Test + public void testInvokeRpcWithInvalidCompositeNode() throws Exception{ + //Thread.sleep(1500); + + ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class); + Assert.assertNotNull(providerRef); + ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef); + Assert.assertNotNull(provider); + ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class); + Assert.assertNotNull(consumerRef); + ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef); + Assert.assertNotNull(consumer); + + // Provider sends announcement + _logger.debug("Provider sends announcement [{}]", QNAME.getLocalName()); + provider.announce(QNAME); + // Consumer invokes RPC + _logger.debug("Invoking RPC [{}]", QNAME); + CompositeNode input = consumer.getInvalidCompositeNodeCompositeChild(); + for (int i = 0; i < 3; i++) { + RpcResult result = consumer.invokeRpc(QNAME, input); + Assert.assertNotNull(result); + _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors()); + Assert.assertFalse(result.isSuccessful()); + Assert.assertNull(result.getResult()); + Assert.assertEquals(1, result.getErrors().size()); + Assert.assertEquals(RpcError.ErrorSeverity.ERROR, ((RpcError)result.getErrors().toArray()[0]).getSeverity()); + } + } + + //@Test + // This method is UNTESTED -- need to get around the bundling issues before I know if this even work +// public void testInvokeRpcWithValidCompositeNode() throws Exception{ +// Thread.sleep(10000); +// //Send announcement +// ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class); +// Assert.assertNotNull(providerRef); +// +// ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef); +// Assert.assertNotNull(provider); +// +// ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class); +// Assert.assertNotNull(consumerRef); +// +// ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef); +// Assert.assertNotNull(consumer); +// +// _logger.debug("Provider sends announcement [{}]", "heartbeat"); +// provider.announce(QNAME); +// ServiceReference routerRef = ctx.getServiceReference(Client.class); +// Client router = (Client) ctx.getService(routerRef); +// _logger.debug("Found router[{}]", router); +// _logger.debug("Invoking RPC [{}]", QNAME); +// for (int i = 0; i < 3; i++) { +// RpcResult result = router.getInstance().invokeRpc(QNAME, consumer.getValidCompositeNodeWithOneSimpleChild()); +// _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors()); +// Assert.assertNotNull(result); +// } +// } + + private Message send(Message msg) throws IOException { + ZMQ.Socket reqSocket = zmqCtx.socket(ZMQ.REQ); + reqSocket.connect("tcp://localhost:5555"); + reqSocket.send(Message.serialize(msg)); + Message response = parseMessage(reqSocket); + + return response; + } + + /** + * @param socket + * @return + */ + private Message parseMessage(ZMQ.Socket socket) { + + Message msg = null; + try { + byte[] bytes = socket.recv(); + _logger.debug("Received bytes:[{}]", bytes.length); + msg = (Message) Message.deserialize(bytes); + } catch (Throwable t) { + t.printStackTrace(); + } + return msg; + } + + + private void printState(){ + Bundle[] b = ctx.getBundles(); + _logger.debug("\n\nNumber of bundles [{}]\n\n]", b.length); + for (int i=0;i + + + 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 + ref_hash-map-data-store + + + + prefix:binding-broker-impl + binding-broker-impl + + binding:binding-notification-service + ref_binding-notification-broker + + + binding:binding-data-broker + ref_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 + ref_dom-broker + + + binding:binding-dom-mapping-service + ref_runtime-mapping-singleton + + + + prefix:remote-zeromq-rpc-server + remoter + 5666 + + prefix:dom-broker-osgi-registry + ref_dom-broker + + + + + + dom:schema-service + + ref_yang-schema-service + /config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service'] + + + + binding:binding-notification-service + + ref_binding-notification-broker + /config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker'] + + + + dom:dom-data-store + + ref_hash-map-data-store + /config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store'] + + + + binding:binding-broker-osgi-registry + + ref_binding-broker-impl + /config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl'] + + + + binding-impl:binding-dom-mapping-service + + ref_runtime-mapping-singleton + /config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton'] + + + + dom:dom-broker-osgi-registry + + ref_dom-broker + /config/modules/module[name='dom-broker-impl']/instance[name='dom-broker'] + + + + binding:binding-data-broker + + ref_binding-data-broker + /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker'] + + + + + + +//END OF SNAPSHOT +urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05 +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:binding?module=opendaylight-md-sal-binding&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:dom?module=opendaylight-md-sal-dom&revision=2013-10-28 +urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc?module=odl-sal-dom-rpc-remote-cfg&revision=2013-10-28 +//END OF CONFIG diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/src/test/resources/logback.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/src/test/resources/logback.xml new file mode 100644 index 0000000000..1d17796373 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-nb/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-nb/pom.xml new file mode 100644 index 0000000000..dd7e36cfb4 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-nb/pom.xml @@ -0,0 +1,109 @@ + + + 4.0.0 + + sal-remoterpc-connector-test-parent + org.opendaylight.controller.tests + 1.0-SNAPSHOT + + + sal-remoterpc-connector-test-nb + bundle + + + + + org.apache.felix + maven-bundle-plugin + ${bundle.plugin.version} + true + + + + + + com.sun.jersey.spi.container.servlet, + org.codehaus.jackson.annotate, + javax.ws.rs, + javax.ws.rs.core, + javax.xml.bind, + javax.xml.bind.annotation, + org.slf4j, + org.apache.catalina.filters, + org.codehaus.jackson.jaxrs, + org.opendaylight.controller.sample.zeromq.provider, + org.opendaylight.controller.sample.zeromq.consumer, + org.opendaylight.controller.sal.utils, + org.opendaylight.yangtools.yang.common, + org.opendaylight.controller.sal.connector.api, + org.opendaylight.controller.sal.connector.remoterpc.api;version="[0.4,1)", + org.opendaylight.controller.sal.connector.remoterpc.impl;version="[0.4,1)", + org.opendaylight.controller.sal.connector.remoterpc.dto, + org.opendaylight.controller.sal.connector.remoterpc.util, + org.osgi.framework, + com.google.common.base, + org.opendaylight.yangtools.yang.data.api, + !org.codehaus.enunciate.jaxrs + + + /controller/nb/v2/zmqnb + ,${classes;ANNOTATION;javax.ws.rs.Path} + + ${project.basedir}/src/main/resources/META-INF + + + + + + + org.opendaylight.controller + containermanager + 0.5.1-SNAPSHOT + + + org.opendaylight.controller + commons.northbound + 0.4.1-SNAPSHOT + + + org.opendaylight.controller + sal + 0.5.1-SNAPSHOT + + + org.opendaylight.controller.tests + sal-remoterpc-connector-test-provider + 1.0-SNAPSHOT + + + org.opendaylight.controller.tests + sal-remoterpc-connector-test-consumer + 1.0-SNAPSHOT + + + org.opendaylight.controller + sal-remoterpc-connector + 1.0-SNAPSHOT + + + org.osgi + org.osgi.core + 5.0.0 + + + junit + junit + + + org.opendaylight.controller + zeromq-routingtable.implementation + 0.4.1-SNAPSHOT + + + com.google.guava + guava + + + + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-nb/src/main/java/org/opendaylight/controller/tests/zmqrouter/rest/Router.java b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-nb/src/main/java/org/opendaylight/controller/tests/zmqrouter/rest/Router.java new file mode 100644 index 0000000000..6c9ec4e788 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-nb/src/main/java/org/opendaylight/controller/tests/zmqrouter/rest/Router.java @@ -0,0 +1,246 @@ +package org.opendaylight.controller.tests.zmqrouter.rest; + +import org.opendaylight.controller.sal.connector.api.RpcRouter; +import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable; +import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException; +import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException; +import org.opendaylight.controller.sal.connector.remoterpc.dto.CompositeNodeImpl; +import org.opendaylight.controller.sal.connector.remoterpc.impl.RoutingTableImpl; +import org.opendaylight.controller.sal.connector.remoterpc.util.XmlUtils; +import org.opendaylight.controller.sample.zeromq.consumer.ExampleConsumer; +import org.opendaylight.controller.sample.zeromq.provider.ExampleProvider; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.osgi.framework.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import java.io.Serializable; +import java.net.URI; +import java.util.Set; + +@Path("router") +public class Router { + private Logger _logger = LoggerFactory.getLogger(Router.class); + private final URI namespace = URI.create("http://cisco.com/example"); + private final QName QNAME = new QName(namespace, "heartbeat"); + + + @GET + @Path("/hello") + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "Hello"; + } + + @GET + @Path("/announce") + @Produces(MediaType.TEXT_PLAIN) + public String announce() { + _logger.info("Announce request received"); + + BundleContext ctx = getBundleContext(); + ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class); + if (providerRef == null) { + _logger.debug("Could not get provider reference"); + return "Could not get provider reference"; + } + + ExampleProvider provider = (ExampleProvider) ctx.getService(providerRef); + if (provider == null) { + _logger.info("Could not get provider service"); + return "Could not get provider service"; + } + + provider.announce(QNAME); + return "Announcement sent "; + + } + + @GET + @Path("/rpc") + @Produces(MediaType.TEXT_PLAIN) + public String invokeRpc() throws Exception { + _logger.info("Invoking RPC"); + + ExampleConsumer consumer = getConsumer(); + RpcResult result = consumer.invokeRpc(QNAME, new CompositeNodeImpl()); + _logger.info("Result [{}]", result.isSuccessful()); + + return stringify(result); + } + + @GET + @Path("/rpc-success") + @Produces(MediaType.TEXT_PLAIN) + public String invokeRpcSuccess() throws Exception { + ExampleConsumer consumer = getConsumer(); + RpcResult result = consumer.invokeRpc(QNAME, consumer.getValidCompositeNodeWithFourSimpleChildren()); //TODO: Change this + _logger.info("Result [{}]", result.isSuccessful()); + + return stringify(result); + } + + @GET + @Path("/rpc-failure") + @Produces(MediaType.TEXT_PLAIN) + public String invokeRpcFailure() throws Exception { + ExampleConsumer consumer = getConsumer(); + //RpcResult result = consumer.invokeRpc(QNAME, consumer.getInvalidCompositeNodeCompositeChild()); //TODO: Change this + RpcResult result = consumer.invokeRpc(QNAME, null); //TODO: Change this + _logger.info("Result [{}]", result.isSuccessful()); + + return stringify(result); + } + + @GET + @Path("/routingtable") + @Produces(MediaType.TEXT_PLAIN) + public String invokeRoutingTable() { + _logger.info("Invoking adding an entry in routing table"); + + BundleContext ctx = getBundleContext(); + ServiceReference routingTableServiceReference = ctx.getServiceReference(RoutingTable.class); + if (routingTableServiceReference == null) { + _logger.debug("Could not get routing table impl reference"); + return "Could not get routingtable referen "; + } + RoutingTable routingTable = (RoutingTableImpl) ctx.getService(routingTableServiceReference); + if (routingTable == null) { + _logger.info("Could not get routing table service"); + return "Could not get routing table service"; + } + + + RoutingIdentifierImpl rii = new RoutingIdentifierImpl(); + try { + routingTable.addGlobalRoute(rii.toString(), "172.27.12.1:5000"); + } catch (RoutingTableException e) { + _logger.error("error in adding routing identifier" + e.getMessage()); + + } catch (SystemException e) { + _logger.error("error in adding routing identifier" + e.getMessage()); + } + + Set routes = routingTable.getRoutes(rii.toString()); + + StringBuilder stringBuilder = new StringBuilder(); + for (String route : routes) { + stringBuilder.append(route); + } + + _logger.info("Result [{}] routes added for route" + rii + stringBuilder.toString()); + + return stringBuilder.toString(); + } + + @GET + @Path("/routingtabledelete") + @Produces(MediaType.TEXT_PLAIN) + public String invokeDeleteRoutingTable() { + _logger.info("Invoking adding an entry in routing table"); + + BundleContext ctx = getBundleContext(); + ServiceReference routingTableServiceReference = ctx.getServiceReference(RoutingTable.class); + if (routingTableServiceReference == null) { + _logger.debug("Could not get routing table impl reference"); + return "Could not get routingtable referen "; + } + RoutingTable routingTable = (RoutingTableImpl) ctx.getService(routingTableServiceReference); + if (routingTable == null) { + _logger.info("Could not get routing table service"); + return "Could not get routing table service"; + } + + + RoutingIdentifierImpl rii = new RoutingIdentifierImpl(); + try { + routingTable.removeGlobalRoute(rii.toString()); + } catch (RoutingTableException e) { + _logger.error("error in adding routing identifier" + e.getMessage()); + + } catch (SystemException e) { + _logger.error("error in adding routing identifier" + e.getMessage()); + } + + Set routes = routingTable.getRoutes(rii.toString()); + + StringBuilder stringBuilder = new StringBuilder(); + if (routes != null) { + for (String route : routes) { + stringBuilder.append(route); + } + } else { + stringBuilder.append(" successfully"); + } + + _logger.info("Result [{}] routes removed for route" + rii + stringBuilder.toString()); + + return stringBuilder.toString(); + } + + private String stringify(RpcResult result) { + CompositeNode node = result.getResult(); + StringBuilder builder = new StringBuilder("result:").append(XmlUtils.compositeNodeToXml(node)).append("\n") + .append("error:").append(result.getErrors()).append("\n"); + + return builder.toString(); + } + + private BundleContext getBundleContext() { + ClassLoader tlcl = Thread.currentThread().getContextClassLoader(); + Bundle bundle = null; + + if (tlcl instanceof BundleReference) { + bundle = ((BundleReference) tlcl).getBundle(); + } else { + _logger.info("Unable to determine the bundle context based on " + + "thread context classloader."); + bundle = FrameworkUtil.getBundle(this.getClass()); + } + return (bundle == null ? null : bundle.getBundleContext()); + } + + private ExampleConsumer getConsumer() { + BundleContext ctx = getBundleContext(); + ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class); + if (consumerRef == null) { + _logger.debug("Could not get consumer reference"); + throw new NullPointerException("Could not get consumer reference"); + } + ExampleConsumer consumer = (ExampleConsumer) ctx.getService(consumerRef); + if (consumer == null) { + _logger.info("Could not get consumer service"); + throw new NullPointerException("Could not get consumer service"); + } + return consumer; + } + + class RoutingIdentifierImpl implements RpcRouter.RouteIdentifier, Serializable { + + private final URI namespace = URI.create("http://cisco.com/example"); + private final QName QNAME = new QName(namespace, "global"); + private final QName instance = new QName(URI.create("127.0.0.1"), "local"); + + @Override + public QName getContext() { + return QNAME; + } + + @Override + public QName getType() { + return QNAME; + } + + @Override + public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier getRoute() { + return InstanceIdentifier.of(instance); + } + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-nb/src/main/resources/WEB-INF/web.xml b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-nb/src/main/resources/WEB-INF/web.xml new file mode 100644 index 0000000000..5bd21398fb --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-nb/src/main/resources/WEB-INF/web.xml @@ -0,0 +1,58 @@ + + + + JAXRSZmq + com.sun.jersey.spi.container.servlet.ServletContainer + + javax.ws.rs.Application + org.opendaylight.controller.northbound.commons.NorthboundApplication + + 1 + + + + JAXRSZmq + /* + + + + + + + NB api + /* + POST + GET + PUT + PATCH + DELETE + HEAD + + + System-Admin + Network-Admin + Network-Operator + Container-User + + + + + System-Admin + + + Network-Admin + + + Network-Operator + + + Container-User + + + + BASIC + opendaylight + + diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java index 7022db2bc9..04114fa0ed 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java @@ -17,6 +17,7 @@ import javax.ws.rs.ext.Provider; import org.opendaylight.controller.sal.rest.api.Draft01; import org.opendaylight.controller.sal.rest.api.Draft02; import org.opendaylight.controller.sal.rest.api.RestconfService; +import org.opendaylight.controller.sal.restconf.impl.ResponseException; import org.opendaylight.controller.sal.restconf.impl.StructuredData; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; @@ -45,7 +46,7 @@ public enum StructuredDataToJsonProvider implements MessageBodyWriter) { if (schema instanceof LeafListSchemaNode) { writeValueOfNodeByType(itemEl, (SimpleNode) data, ((LeafListSchemaNode) schema).getType()); @@ -73,11 +72,14 @@ public class XmlMapper { } } else { // CompositeNode for (Node child : ((CompositeNode) data).getChildren()) { - DataSchemaNode childSchema = findFirstSchemaForNode(child, ((DataNodeContainer) schema).getChildNodes()); - if (logger.isDebugEnabled()) { - if (childSchema == null) { - logger.debug("Probably the data node \"" + ((child == null) ? "" : child.getNodeType().getLocalName()) - + "\" is not conform to schema"); + DataSchemaNode childSchema = null; + if(schema != null){ + childSchema = findFirstSchemaForNode(child, ((DataNodeContainer) schema).getChildNodes()); + if (logger.isDebugEnabled()) { + if (childSchema == null) { + logger.debug("Probably the data node \"" + ((child == null) ? "" : child.getNodeType().getLocalName()) + + "\" is not conform to schema"); + } } } itemEl.appendChild(translateToXmlAndReturnRootElement(doc, child, childSchema)); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.xtend index 1b0fda41ca..df6b58c897 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.xtend @@ -9,9 +9,12 @@ import org.opendaylight.yangtools.yang.common.QName import org.opendaylight.yangtools.yang.common.RpcResult import org.opendaylight.yangtools.yang.data.api.CompositeNode import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier +import org.slf4j.LoggerFactory class BrokerFacade implements DataReader { + + val static LOG = LoggerFactory.getLogger(BrokerFacade) val static BrokerFacade INSTANCE = new BrokerFacade @Property @@ -38,11 +41,13 @@ class BrokerFacade implements DataReader { override readConfigurationData(InstanceIdentifier path) { checkPreconditions + LOG.info("Read Configuration via Restconf: {}",path) return dataService.readConfigurationData(path); } override readOperationalData(InstanceIdentifier path) { checkPreconditions + LOG.info("Read Operational via Restconf: {}",path) return dataService.readOperationalData(path); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend index 602e8b9242..fed56fe297 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend @@ -33,6 +33,7 @@ import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil import static com.google.common.base.Preconditions.* import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec +import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition class ControllerContext implements SchemaServiceListener { @@ -291,7 +292,16 @@ class ControllerContext implements SchemaServiceListener { checkArgument(node instanceof LeafSchemaNode); val urlDecoded = URLDecoder.decode(uriValue); val typedef = (node as LeafSchemaNode).type; - val decoded = TypeDefinitionAwareCodec.from(typedef)?.deserialize(urlDecoded) + var decoded = TypeDefinitionAwareCodec.from(typedef)?.deserialize(urlDecoded) + if(decoded == null) { + var baseType = typedef + while (baseType.baseType != null) { + baseType = baseType.baseType; + } + if(baseType instanceof IdentityrefTypeDefinition) { + decoded = toQName(urlDecoded) + } + } map.put(node.QName, decoded); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend index 35352e0819..94a7756da6 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend @@ -18,9 +18,10 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode import org.opendaylight.yangtools.yang.common.QName import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode +import static javax.ws.rs.core.Response.Status.* class RestconfImpl implements RestconfService { - + val static RestconfImpl INSTANCE = new RestconfImpl @Property @@ -28,13 +29,13 @@ class RestconfImpl implements RestconfService { @Property extension ControllerContext controllerContext - + private new() { if (INSTANCE !== null) { throw new IllegalStateException("Already instantiated"); } } - + static def getInstance() { return INSTANCE } @@ -61,141 +62,148 @@ class RestconfImpl implements RestconfService { override createConfigurationData(String identifier, CompositeNode payload) { val identifierWithSchemaNode = identifier.resolveInstanceIdentifier val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode) - val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get(); + val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier, value).get(); switch status.result { - case TransactionStatus.COMMITED: Response.status(Response.Status.OK).build - default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build + case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build + default: Response.status(INTERNAL_SERVER_ERROR).build } } override updateConfigurationData(String identifier, CompositeNode payload) { val identifierWithSchemaNode = identifier.resolveInstanceIdentifier val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode) - val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get(); + val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier, value).get(); switch status.result { - case TransactionStatus.COMMITED: Response.status(Response.Status.NO_CONTENT).build - default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build + case TransactionStatus.COMMITED: Response.status(OK).build + default: Response.status(INTERNAL_SERVER_ERROR).build } } override invokeRpc(String identifier, CompositeNode payload) { val rpc = identifier.rpcDefinition if (rpc === null) { - throw new ResponseException(Response.Status.NOT_FOUND, "RPC does not exist."); + throw new ResponseException(NOT_FOUND, "RPC does not exist."); } val value = normalizeNode(payload, rpc.input) val List> input = new ArrayList input.add(value) val rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null) val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest); - return new StructuredData(rpcResult.result, rpc.output); + if (!rpcResult.successful) { + throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed") + } + if (rpcResult.result === null) { + return null + } + return new StructuredData(rpcResult.result, rpc.output) } - + override readConfigurationData(String identifier) { val instanceIdentifierWithSchemaNode = identifier.resolveInstanceIdentifier val data = broker.readConfigurationData(instanceIdentifierWithSchemaNode.getInstanceIdentifier); return new StructuredData(data, instanceIdentifierWithSchemaNode.schemaNode) } - + override readOperationalData(String identifier) { val instanceIdentifierWithSchemaNode = identifier.resolveInstanceIdentifier val data = broker.readOperationalData(instanceIdentifierWithSchemaNode.getInstanceIdentifier); return new StructuredData(data, instanceIdentifierWithSchemaNode.schemaNode) } - + override updateConfigurationDataLegacy(String identifier, CompositeNode payload) { - updateConfigurationData(identifier,payload); + updateConfigurationData(identifier, payload); } - + override createConfigurationDataLegacy(String identifier, CompositeNode payload) { - createConfigurationData(identifier,payload); + createConfigurationData(identifier, payload); } - + private def InstanceIdWithSchemaNode resolveInstanceIdentifier(String identifier) { val identifierWithSchemaNode = identifier.toInstanceIdentifier if (identifierWithSchemaNode === null) { - throw new ResponseException(Response.Status.BAD_REQUEST, "URI has bad format"); + throw new ResponseException(BAD_REQUEST, "URI has bad format"); } return identifierWithSchemaNode } - + private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema) { if (node instanceof CompositeNodeWrapper) { - normalizeNode(node as CompositeNodeWrapper, schema,null) + normalizeNode(node as CompositeNodeWrapper, schema, null) return (node as CompositeNodeWrapper).unwrap() } return node } - private def void normalizeNode(NodeWrapper nodeBuilder, DataSchemaNode schema,QName previousAugment) { + private def void normalizeNode(NodeWrapper nodeBuilder, DataSchemaNode schema, QName previousAugment) { if (schema === null) { - throw new ResponseException(Response.Status.BAD_REQUEST, + throw new ResponseException(BAD_REQUEST, "Data has bad format\n" + nodeBuilder.localName + " does not exist in yang schema."); } var validQName = schema.QName var currentAugment = previousAugment; - if(schema.augmenting) { + if (schema.augmenting) { currentAugment = schema.QName - } else if(previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) { - validQName = QName.create(currentAugment,schema.QName.localName); + } else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) { + validQName = QName.create(currentAugment, schema.QName.localName); } val moduleName = controllerContext.findModuleByNamespace(validQName.namespace); if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace || nodeBuilder.namespace.path == moduleName) { nodeBuilder.qname = validQName } else { - throw new ResponseException(Response.Status.BAD_REQUEST, + throw new ResponseException(BAD_REQUEST, "Data has bad format\nIf data is in XML format then namespace for " + nodeBuilder.localName + " should be " + schema.QName.namespace + ".\n If data is in Json format then module name for " + nodeBuilder.localName + " should be " + moduleName + "."); } - + if (nodeBuilder instanceof CompositeNodeWrapper) { val List> children = (nodeBuilder as CompositeNodeWrapper).getValues for (child : children) { normalizeNode(child, - findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes),currentAugment) + findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes), + currentAugment) } } else if (nodeBuilder instanceof SimpleNodeWrapper) { val simpleNode = (nodeBuilder as SimpleNodeWrapper) val stringValue = simpleNode.value as String; - + val objectValue = TypeDefinitionAwareCodec.from(schema.typeDefinition)?.deserialize(stringValue); simpleNode.setValue(objectValue) } else if (nodeBuilder instanceof EmptyNodeWrapper) { val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper - if(schema instanceof LeafSchemaNode) { + if (schema instanceof LeafSchemaNode) { emptyNodeBuilder.setComposite(false); - } else if(schema instanceof ContainerSchemaNode) { + } else if (schema instanceof ContainerSchemaNode) { + // FIXME: Add presence check emptyNodeBuilder.setComposite(true); } } } - + private def dispatch TypeDefinition typeDefinition(LeafSchemaNode node) { node.type } - + private def dispatch TypeDefinition typeDefinition(LeafListSchemaNode node) { node.type } - - + private def DataSchemaNode findFirstSchemaByLocalName(String localName, Set schemas) { for (schema : schemas) { if (schema instanceof ChoiceNode) { for (caze : (schema as ChoiceNode).cases) { - val result = findFirstSchemaByLocalName(localName, caze.childNodes) + val result = findFirstSchemaByLocalName(localName, caze.childNodes) if (result !== null) { return result } } } else { val result = schemas.findFirst[n|n.QName.localName.equals(localName)] - if(result !== null) { + if (result !== null) { return result; - + } } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java index 103c9ed3cd..d58b7e9dab 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java @@ -27,7 +27,7 @@ public class InvokeRpcMethodTest { @Override public RpcResult answer(InvocationOnMock invocation) throws Throwable { CompositeNode compNode = (CompositeNode) invocation.getArguments()[1]; - return new DummyRpcResult.Builder().result(compNode).build(); + return new DummyRpcResult.Builder().result(compNode).isSuccessful(true).build(); } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java index 5b0eea3212..ec7dba67b9 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java @@ -133,21 +133,21 @@ public class XmlProvidersTest extends JerseyTest { String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0"); Response response = target(uri).request(MEDIA_TYPE_DRAFT02).put(entity); - assertEquals(204, response.getStatus()); - response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity); assertEquals(200, response.getStatus()); + response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity); + assertEquals(204, response.getStatus()); uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0"); response = target(uri).request(MEDIA_TYPE_DRAFT02).put(entity); - assertEquals(204, response.getStatus()); - response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity); assertEquals(200, response.getStatus()); + response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity); + assertEquals(204, response.getStatus()); uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0"); response = target(uri).request(MEDIA_TYPE).put(entity); - assertEquals(204, response.getStatus()); - response = target(uri).request(MEDIA_TYPE).post(entity); assertEquals(200, response.getStatus()); + response = target(uri).request(MEDIA_TYPE).post(entity); + assertEquals(204, response.getStatus()); } @Test diff --git a/opendaylight/md-sal/sal-zeromq-connector/pom.xml b/opendaylight/md-sal/sal-zeromq-connector/pom.xml deleted file mode 100644 index 7859908768..0000000000 --- a/opendaylight/md-sal/sal-zeromq-connector/pom.xml +++ /dev/null @@ -1,149 +0,0 @@ - - - 4.0.0 - - org.opendaylight.controller - sal-parent - 1.0-SNAPSHOT - - - sal-zeromq-connector - bundle - - - 2.10.3 - - - - - - org.apache.felix - maven-bundle-plugin - true - - - - org.opendaylight.controller.sal.connector.api, - org.opendaylight.controller.sal.core.api, - org.opendaylight.yangtools.concepts;version="[0.1,1)", - org.opendaylight.yangtools.yang.common;version="[0.5,1)", - org.opendaylight.yangtools.yang.data.api;version="[0.5,1)", - org.zeromq;version="[0.3,1)" - - org.opendaylight.controller.sal.connector.remoterpc.router.zeromq.Activator - - - - - - net.alchim31.maven - scala-maven-plugin - 3.1.6 - - incremental - - -target:jvm-1.7 - - - -source1.7 - -target1.7 - - - - - scala-compile - - compile - - - - scala-test-compile - - testCompile - - - - - - - maven-compiler-plugin - - - default-compile - none - - - default-testCompile - none - - - - - - - - - org.scala-lang - scala-library - ${scala.version} - - - - org.opendaylight.controller - containermanager - 0.5.1-SNAPSHOT - - - org.opendaylight.controller - commons.northbound - 0.4.1-SNAPSHOT - - - org.opendaylight.controller - sal - 0.5.1-SNAPSHOT - - - org.opendaylight.yangtools - yang-binding - - - org.opendaylight.yangtools - yang-common - - - org.opendaylight.controller - sal-connector-api - - - org.opendaylight.controller - sal-common-util - 1.0-SNAPSHOT - - - - junit - junit - - - org.jeromq - jeromq - 0.3.0-SNAPSHOT - - - - - - sonatype-nexus-snapshots - https://oss.sonatype.org/content/repositories/snapshots - - false - - - true - - - - - diff --git a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/api/RouteChange.java b/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/api/RouteChange.java deleted file mode 100644 index ba90f3705f..0000000000 --- a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/api/RouteChange.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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.connector.remoterpc.api; - -import java.util.Map; -import java.util.Set; - -public interface RouteChange { - - Map> getRemovals(); - Map> getAnnouncements(); -} diff --git a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/Activator.java b/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/Activator.java deleted file mode 100644 index 5b927a56b1..0000000000 --- a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/Activator.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.connector.remoterpc.router.zeromq; - -import org.opendaylight.controller.sal.core.api.AbstractProvider; -import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; -import org.osgi.framework.BundleContext; - -public class Activator extends AbstractProvider { - - ZeroMqRpcRouter router; - - @Override - public void onSessionInitiated(ProviderSession session) { - router = ZeroMqRpcRouter.getInstance(); - router.setBrokerSession(session); - router.start(); - } - - @Override - protected void stopImpl(BundleContext context) { - router.stop(); - } - -} diff --git a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/ZeroMqRpcRouter.java b/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/ZeroMqRpcRouter.java deleted file mode 100644 index af94804322..0000000000 --- a/opendaylight/md-sal/sal-zeromq-connector/src/main/java/org/opendaylight/controller/sal/connector/remoterpc/router/zeromq/ZeroMqRpcRouter.java +++ /dev/null @@ -1,450 +0,0 @@ -/* - * 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.connector.remoterpc.router.zeromq; - -import java.io.IOException; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import org.opendaylight.controller.sal.connector.api.RpcRouter; -import org.opendaylight.controller.sal.connector.remoterpc.router.zeromq.Message.MessageType; -import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; -import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration; -import org.opendaylight.controller.sal.core.api.RpcImplementation; -import org.opendaylight.controller.sal.core.api.RpcRegistrationListener; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.zeromq.ZMQ; - -/** - * ZeroMq based implementation of RpcRouter - * TODO: - * 1. Make it multi VM aware - * 2. Make rpc request handling async and non-blocking. Note zmq socket is not thread safe - * 3. sendRpc() should use connection pooling - * 4. Read properties from config file using existing(?) ODL properties framework - */ -public class ZeroMqRpcRouter implements RpcRouter { - - private ExecutorService serverPool; - private static ExecutorService handlersPool; - - private Map, String> routingTable; - - private ProviderSession brokerSession; - - private ZMQ.Context context; - private ZMQ.Socket publisher; - private ZMQ.Socket subscriber; - private ZMQ.Socket replySocket; - - private static ZeroMqRpcRouter _instance = new ZeroMqRpcRouter(); - - private final RpcFacade facade = new RpcFacade(); - private final RpcListener listener = new RpcListener(); - - private final String localIp = getLocalIpAddress(); - - private String pubPort = System.getProperty("pub.port");// port on which announcements are sent - private String subPort = System.getProperty("sub.port");// other controller's pub port - private String pubIp = System.getProperty("pub.ip"); // other controller's ip - private String rpcPort = System.getProperty("rpc.port");// port on which RPC messages are received - - private Logger _logger = LoggerFactory.getLogger(ZeroMqRpcRouter.class); - - //Prevent instantiation - private ZeroMqRpcRouter() { - } - - public static ZeroMqRpcRouter getInstance() { - return _instance; - } - - public void start() { - context = ZMQ.context(2); - publisher = context.socket(ZMQ.PUB); - int ret = publisher.bind("tcp://*:" + pubPort); - // serverPool = Executors.newSingleThreadExecutor(); - serverPool = Executors.newCachedThreadPool(); - handlersPool = Executors.newCachedThreadPool(); - routingTable = new ConcurrentHashMap, String>(); - - // Start listening for announce and rpc messages - serverPool.execute(receive()); - - brokerSession.addRpcRegistrationListener(listener); - - Set currentlySupported = brokerSession.getSupportedRpcs(); - for (QName rpc : currentlySupported) { - listener.onRpcImplementationAdded(rpc); - } - - } - - public void stop() { - if (handlersPool != null) - handlersPool.shutdown(); - if (serverPool != null) - serverPool.shutdown(); - if (publisher != null) { - publisher.setLinger(0); - publisher.close(); - } - if (replySocket != null) { - replySocket.setLinger(0); - replySocket.close(); - } - if (subscriber != null) { - subscriber.setLinger(0); - subscriber.close(); - } - if (context != null) - context.term(); - - } - - private Runnable receive() { - return new Runnable() { - public void run() { - try { - // Bind to RPC reply socket - replySocket = context.socket(ZMQ.REP); - replySocket.bind("tcp://*:" + rpcPort); - - // Bind to publishing controller - subscriber = context.socket(ZMQ.SUB); - String pubAddress = "tcp://" + pubIp + ":" + subPort; - subscriber.connect(pubAddress); - _logger.debug("{} Subscribing at[{}]", Thread.currentThread().getName(), pubAddress); - - //subscribe for announcements - //TODO: Message type would be changed. Update this - subscriber.subscribe(Message.serialize(Message.MessageType.ANNOUNCE)); - - // Poller enables listening on multiple sockets using a single thread - ZMQ.Poller poller = new ZMQ.Poller(2); - poller.register(replySocket, ZMQ.Poller.POLLIN); - poller.register(subscriber, ZMQ.Poller.POLLIN); - - //TODO: Add code to restart the thread after exception - while (!Thread.currentThread().isInterrupted()) { - - poller.poll(); - - if (poller.pollin(0)) { - handleRpcCall(); - } - if (poller.pollin(1)) { - handleAnnouncement(); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - replySocket.setLinger(0); - replySocket.close(); - subscriber.setLinger(0); - subscriber.close(); - } - }; - } - - /** - * @throws IOException - * @throws ClassNotFoundException - */ - private void handleAnnouncement() throws IOException, ClassNotFoundException { - - _logger.info("Announcement received"); - Message.MessageType topic = (MessageType) Message.deserialize(subscriber.recv()); - - if (subscriber.hasReceiveMore()) { - try { - Message m = (Message) Message.deserialize(subscriber.recv()); - _logger.debug("Announcement message [{}]", m); - - // TODO: check on msg type or topic. Both - // should be same. Need to normalize. - if (Message.MessageType.ANNOUNCE == m.getType()) - updateRoutingTable(m); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - } - } - - } - - /** - * @throws InterruptedException - * @throws ExecutionException - */ - private void handleRpcCall() throws InterruptedException, ExecutionException { - try { - Message request = parseMessage(replySocket); - - _logger.debug("Received rpc request [{}]", request); - - // Call broker to process the message then reply - Future> rpc = brokerSession.rpc( - (QName) request.getRoute().getType(), (CompositeNode) request.getPayload()); - - RpcResult result = rpc.get(); - - Message response = new Message.MessageBuilder() - .type(MessageType.RESPONSE) - .sender(localIp + ":" + rpcPort) - .route(request.getRoute()) - //.payload(result) TODO: enable and test - .build(); - - replySocket.send(Message.serialize(response)); - - _logger.debug("Sent rpc response [{}]", response); - - } catch (IOException ex) { - //TODO: handle exception and send error codes to caller - ex.printStackTrace(); - } - } - - - @Override - public Future> sendRpc( - final RpcRequest input) { - - return handlersPool.submit(new Callable>() { - - @Override - public RpcReply call() { - ZMQ.Socket requestSocket = context.socket(ZMQ.REQ); - - // TODO pick the ip and port from routing table based on routing identifier - requestSocket.connect("tcp://" + pubIp + ":5554"); - - Message requestMessage = new Message.MessageBuilder() - .type(MessageType.REQUEST) - .sender(localIp + ":" + rpcPort) - .route(input.getRoutingInformation()) - .payload(input.getPayload()) - .build(); - - _logger.debug("Sending rpc request [{}]", requestMessage); - - RpcReply reply = null; - - try { - - requestSocket.send(Message.serialize(requestMessage)); - final Message response = parseMessage(requestSocket); - - _logger.debug("Received response [{}]", response); - - reply = new RpcReply() { - - @Override - public Object getPayload() { - return response.getPayload(); - } - }; - } catch (IOException ex) { - // TODO: Pass exception back to the caller - ex.printStackTrace(); - } - - return reply; - } - }); - } - - /** - * TODO: Remove this implementation and use RoutingTable implementation to send announcements - * Publishes a notice to other controllers in the cluster - * - * @param notice - */ - public void publish(final Message notice) { - Runnable task = new Runnable() { - public void run() { - - try { - - publisher.sendMore(Message.serialize(Message.MessageType.ANNOUNCE)); - publisher.send(Message.serialize(notice)); - _logger.debug("Announcement sent [{}]", notice); - } catch (IOException ex) { - _logger.error("Error in sending announcement [{}]", notice); - ex.printStackTrace(); - } - } - }; - handlersPool.execute(task); - } - - /** - * Finds IPv4 address of the local VM - * TODO: This method is non-deterministic. There may be more than one IPv4 address. Cant say which - * address will be returned. Read IP from a property file or enhance the code to make it deterministic. - * Should we use IP or hostname? - * - * @return - */ - private String getLocalIpAddress() { - String hostAddress = null; - Enumeration e = null; - try { - e = NetworkInterface.getNetworkInterfaces(); - } catch (SocketException e1) { - e1.printStackTrace(); - } - while (e.hasMoreElements()) { - - NetworkInterface n = (NetworkInterface) e.nextElement(); - - Enumeration ee = n.getInetAddresses(); - while (ee.hasMoreElements()) { - InetAddress i = (InetAddress) ee.nextElement(); - if ((i instanceof Inet4Address) && (i.isSiteLocalAddress())) - hostAddress = i.getHostAddress(); - } - } - return hostAddress; - - } - - /** - * TODO: Change to use external routing table implementation - * - * @param msg - */ - private void updateRoutingTable(Message msg) { - routingTable.put(msg.getRoute(), msg.getSender()); - RpcRouter.RouteIdentifier route = msg.getRoute(); - - // Currently only registers rpc implementation. - // TODO: do registration for instance based routing - QName rpcType = route.getType(); - RpcRegistration registration = brokerSession.addRpcImplementation(rpcType, facade); - _logger.debug("Routing table updated"); - } - - /** - * @param socket - * @return - */ - private Message parseMessage(ZMQ.Socket socket) { - - Message msg = null; - try { - byte[] bytes = socket.recv(); - _logger.debug("Received bytes:[{}]", bytes.length); - msg = (Message) Message.deserialize(bytes); - } catch (Throwable t) { - t.printStackTrace(); - } - return msg; - } - - private class RpcFacade implements RpcImplementation { - - @Override - public Set getSupportedRpcs() { - return Collections.emptySet(); - } - - @Override - public RpcResult invokeRpc(QName rpc, CompositeNode input) { - - RouteIdentifierImpl routeId = new RouteIdentifierImpl(); - routeId.setType(rpc); - - RpcRequestImpl request = new RpcRequestImpl(); - request.setRouteIdentifier(routeId); - request.setPayload(input); - - final Future> ret = sendRpc(request); - - //TODO: Review result handling - RpcResult result = new RpcResult() { - @Override - public boolean isSuccessful() { - try { - ret.get(); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - public CompositeNode getResult() { - return null; - } - - @Override - public Collection getErrors() { - return Collections.EMPTY_LIST; - } - }; - return result; - } - } - - /** - * Listener for rpc registrations - */ - private class RpcListener implements RpcRegistrationListener { - - @Override - public void onRpcImplementationAdded(QName name) { - - _logger.debug("Announcing registration for [{}]", name); - RouteIdentifierImpl routeId = new RouteIdentifierImpl(); - routeId.setType(name); - - //TODO: Make notice immutable and change message type - Message notice = new Message.MessageBuilder() - .type(MessageType.ANNOUNCE) - .sender("tcp://" + localIp + ":" + rpcPort) - .route(routeId) - .build(); - - publish(notice); - } - - @Override - public void onRpcImplementationRemoved(QName name) { - // TODO: send a rpc-deregistrtation notice - - } - } - - public void setBrokerSession(ProviderSession session) { - this.brokerSession = session; - - } - -} diff --git a/opendaylight/md-sal/test/pom.xml b/opendaylight/md-sal/test/pom.xml deleted file mode 100644 index f9e500ea2b..0000000000 --- a/opendaylight/md-sal/test/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - 4.0.0 - - sal-parent - 1.0-SNAPSHOT - org.opendaylight.controller - - pom - org.opendaylight.controller.tests - sal-test-parent - - scm:git:ssh://git.opendaylight.org:29418/controller.git - scm:git:ssh://git.opendaylight.org:29418/controller.git - https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL - - - - zeromq-test-consumer - zeromq-test-it - zeromq-test-provider - - - diff --git a/opendaylight/md-sal/test/zeromq-test-consumer/src/main/java/org/opendaylight/controller/sample/zeromq/consumer/ExampleConsumer.java b/opendaylight/md-sal/test/zeromq-test-consumer/src/main/java/org/opendaylight/controller/sample/zeromq/consumer/ExampleConsumer.java deleted file mode 100644 index a56a7dedff..0000000000 --- a/opendaylight/md-sal/test/zeromq-test-consumer/src/main/java/org/opendaylight/controller/sample/zeromq/consumer/ExampleConsumer.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.opendaylight.controller.sample.zeromq.consumer; - -import java.net.URI; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import org.opendaylight.controller.sal.core.api.AbstractConsumer; -import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.osgi.framework.BundleContext; - -public class ExampleConsumer extends AbstractConsumer { - - private final URI namespace = URI.create("http://cisco.com/example"); - private final QName QNAME = new QName(namespace,"heartbeat"); - - ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); - private ConsumerSession session; - - - @Override - public void onSessionInitiated(ConsumerSession session) { - this.session = session; - executor.scheduleAtFixedRate(new Runnable() { - - @Override - public void run() { - int count = 0; - try { - Future> future = ExampleConsumer.this.session.rpc(QNAME, null); - RpcResult result = future.get(); - System.out.println("Result received. Status is :" + result.isSuccessful()); - } catch (Exception e) { - e.printStackTrace(); - } - - } - }, 0, 10, TimeUnit.SECONDS); - } - - @Override - protected void stopImpl(BundleContext context) { - // TODO Auto-generated method stub - super.stopImpl(context); - executor.shutdown(); - } -} diff --git a/opendaylight/md-sal/test/zeromq-test-it/pom.xml b/opendaylight/md-sal/test/zeromq-test-it/pom.xml deleted file mode 100644 index 56945d1d34..0000000000 --- a/opendaylight/md-sal/test/zeromq-test-it/pom.xml +++ /dev/null @@ -1,184 +0,0 @@ - - 4.0.0 - - sal-test-parent - org.opendaylight.controller.tests - 1.0-SNAPSHOT - - zeromq-test-it - - scm:git:ssh://git.opendaylight.org:29418/controller.git - scm:git:ssh://git.opendaylight.org:29418/controller.git - https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL - - - - 3.0.0 - 1.5.0 - - - - - - org.ops4j.pax.exam - maven-paxexam-plugin - 1.2.4 - - - generate-config - - generate-depends-file - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.ops4j.pax.exam - - - maven-paxexam-plugin - - - [1.2.4,) - - - - generate-depends-file - - - - - - - - - - - - - - - - - - org.opendaylight.yangtools.thirdparty - xtend-lib-osgi - 2.4.3 - - - org.opendaylight.controller.tests - zeromq-test-provider - 1.0-SNAPSHOT - - - org.opendaylight.controller.tests - zeromq-test-consumer - 1.0-SNAPSHOT - - - org.opendaylight.controller - sal-broker-impl - 1.0-SNAPSHOT - - - org.ops4j.pax.exam - pax-exam-container-native - ${exam.version} - test - - - org.ops4j.pax.exam - pax-exam-junit4 - ${exam.version} - test - - - org.ops4j.pax.exam - pax-exam-link-mvn - ${exam.version} - test - - - equinoxSDK381 - org.eclipse.osgi - 3.8.1.v20120830-144521 - test - - - org.slf4j - log4j-over-slf4j - 1.7.2 - - - ch.qos.logback - logback-core - 1.0.9 - - - ch.qos.logback - logback-classic - 1.0.9 - - - org.opendaylight.controller - sal-binding-api - 1.0-SNAPSHOT - - - org.opendaylight.controller - sal-common-util - 1.0-SNAPSHOT - - - org.opendaylight.controller - sal-core-api - 1.0-SNAPSHOT - - - - - org.opendaylight.controller - containermanager - 0.5.1-SNAPSHOT - - - - org.opendaylight.controller - sal - 0.5.1-SNAPSHOT - - - org.opendaylight.yangtools - yang-binding - - - org.opendaylight.yangtools - yang-common - - - org.opendaylight.yangtools - yang-data-api - - - - org.opendaylight.controller - sal-common-util - 1.0-SNAPSHOT - - - diff --git a/opendaylight/md-sal/test/zeromq-test-it/src/test/java/org/opendaylight/controller/sample/zeromq/test/it/ServiceConsumerController.java b/opendaylight/md-sal/test/zeromq-test-it/src/test/java/org/opendaylight/controller/sample/zeromq/test/it/ServiceConsumerController.java deleted file mode 100644 index c17b143d70..0000000000 --- a/opendaylight/md-sal/test/zeromq-test-it/src/test/java/org/opendaylight/controller/sample/zeromq/test/it/ServiceConsumerController.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.opendaylight.controller.sample.zeromq.test.it; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.ops4j.pax.exam.Configuration; -import org.ops4j.pax.exam.Option; -import org.ops4j.pax.exam.junit.PaxExam; -import org.osgi.framework.BundleContext; - -import javax.inject.Inject; - -import static org.junit.Assert.assertTrue; -import static org.ops4j.pax.exam.CoreOptions.*; - -@RunWith(PaxExam.class) -public class ServiceConsumerController { - - public static final String ODL = "org.opendaylight.controller"; - public static final String YANG = "org.opendaylight.yangtools"; - public static final String SAMPLE = "org.opendaylight.controller.samples"; - - @Test - public void properInitialized() throws Exception { - - Thread.sleep(30000); // Waiting for services to get wired. - assertTrue(true); - //assertTrue(consumer.createToast(WhiteBread.class, 5)); - - } - -// @Inject -// BindingAwareBroker broker; - -// @Inject -// ToastConsumer consumer; - - @Inject - BundleContext ctx; - - @Configuration - public Option[] config() { - return options(systemProperty("osgi.console").value("2401"), - systemProperty("pub.port").value("5557"), - systemProperty("sub.port").value("5556"), - systemProperty("rpc.port").value("5555"), - systemProperty("pub.ip").value("localhost"), - mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), // - mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), // - mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), // - mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), // - - //mavenBundle(ODL, "sal-binding-broker-impl").versionAsInProject().update(), // - mavenBundle(ODL, "sal-common").versionAsInProject(), // - mavenBundle(ODL, "sal-common-api").versionAsInProject(),// - mavenBundle(ODL, "sal-common-impl").versionAsInProject(), // - mavenBundle(ODL, "sal-common-util").versionAsInProject(), // - mavenBundle(ODL, "sal-core-api").versionAsInProject().update(), // - mavenBundle(ODL, "sal-broker-impl").versionAsInProject(), // - mavenBundle(ODL, "sal-core-spi").versionAsInProject().update(), // - mavenBundle(ODL, "sal-connector-api").versionAsInProject(), // - mavenBundle(SAMPLE, "zeromq-test-consumer").versionAsInProject(), // - mavenBundle(ODL, "sal-zeromq-connector").versionAsInProject(), // - mavenBundle(YANG, "concepts").versionAsInProject(), - mavenBundle(YANG, "yang-binding").versionAsInProject(), // - mavenBundle(YANG, "yang-common").versionAsInProject(), // - mavenBundle(YANG, "yang-data-api").versionAsInProject(), // - mavenBundle(YANG, "yang-model-api").versionAsInProject(), // - mavenBundle(YANG+".thirdparty", "xtend-lib-osgi").versionAsInProject(), // - mavenBundle("com.google.guava", "guava").versionAsInProject(), // - mavenBundle("org.jeromq", "jeromq").versionAsInProject(), - junitBundles() - ); - } - -} diff --git a/opendaylight/md-sal/test/zeromq-test-it/src/test/java/org/opendaylight/controller/sample/zeromq/test/it/ServiceProviderController.java b/opendaylight/md-sal/test/zeromq-test-it/src/test/java/org/opendaylight/controller/sample/zeromq/test/it/ServiceProviderController.java deleted file mode 100644 index 2d28b0b5b5..0000000000 --- a/opendaylight/md-sal/test/zeromq-test-it/src/test/java/org/opendaylight/controller/sample/zeromq/test/it/ServiceProviderController.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.opendaylight.controller.sample.zeromq.test.it; - -import static org.junit.Assert.*; -import static org.ops4j.pax.exam.CoreOptions.junitBundles; -import static org.ops4j.pax.exam.CoreOptions.mavenBundle; -import static org.ops4j.pax.exam.CoreOptions.options; -import static org.ops4j.pax.exam.CoreOptions.systemPackages; -import static org.ops4j.pax.exam.CoreOptions.systemProperty; -import static org.ops4j.pax.exam.CoreOptions.maven; - -import java.util.Collection; - -import javax.inject.Inject; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; -import org.ops4j.pax.exam.Configuration; -import org.ops4j.pax.exam.CoreOptions; -import org.ops4j.pax.exam.Option; -import org.ops4j.pax.exam.junit.PaxExam; -import org.osgi.framework.BundleContext; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; - -@RunWith(PaxExam.class) -public class ServiceProviderController { - - public static final String ODL = "org.opendaylight.controller"; - public static final String YANG = "org.opendaylight.yangtools"; - public static final String SAMPLE = "org.opendaylight.controller.samples"; - - @Test - public void properInitialized() throws Exception { - - Thread.sleep(30000); // Waiting for services to get wired. - assertTrue(true); - //assertTrue(consumer.createToast(WhiteBread.class, 5)); - - } - -// @Inject -// BindingAwareBroker broker; - -// @Inject -// ToastConsumer consumer; - - @Inject - BundleContext ctx; - - @Configuration - public Option[] config() { - return options(systemProperty("osgi.console").value("2401"), - systemProperty("pub.port").value("5556"), - systemProperty("sub.port").value("5557"), - systemProperty("rpc.port").value("5554"), - systemProperty("pub.ip").value("localhost"), - mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), // - mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), // - mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), // - mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), // - - //mavenBundle(ODL, "sal-binding-broker-impl").versionAsInProject().update(), // - mavenBundle(ODL, "sal-common").versionAsInProject(), // - mavenBundle(ODL, "sal-common-api").versionAsInProject(),// - mavenBundle(ODL, "sal-common-impl").versionAsInProject(), // - mavenBundle(ODL, "sal-common-util").versionAsInProject(), // - mavenBundle(ODL, "sal-core-api").versionAsInProject().update(), // - mavenBundle(ODL, "sal-broker-impl").versionAsInProject(), // - mavenBundle(ODL, "sal-core-spi").versionAsInProject().update(), // - mavenBundle(ODL, "sal-connector-api").versionAsInProject(), // - mavenBundle(SAMPLE, "zeromq-test-provider").versionAsInProject(), // - mavenBundle(ODL, "sal-zeromq-connector").versionAsInProject(), // - mavenBundle(YANG, "concepts").versionAsInProject(), - mavenBundle(YANG, "yang-binding").versionAsInProject(), // - mavenBundle(YANG, "yang-common").versionAsInProject(), // - mavenBundle(YANG, "yang-data-api").versionAsInProject(), // - mavenBundle(YANG, "yang-model-api").versionAsInProject(), // - mavenBundle(YANG+".thirdparty", "xtend-lib-osgi").versionAsInProject(), // - mavenBundle("com.google.guava", "guava").versionAsInProject(), // - mavenBundle("org.jeromq", "jeromq").versionAsInProject(), - junitBundles() - ); - } - -} diff --git a/opendaylight/md-sal/test/zeromq-test-provider/src/main/java/org/opendaylight/controller/sample/zeromq/provider/ExampleProvider.java b/opendaylight/md-sal/test/zeromq-test-provider/src/main/java/org/opendaylight/controller/sample/zeromq/provider/ExampleProvider.java deleted file mode 100644 index ec7d7a8285..0000000000 --- a/opendaylight/md-sal/test/zeromq-test-provider/src/main/java/org/opendaylight/controller/sample/zeromq/provider/ExampleProvider.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.opendaylight.controller.sample.zeromq.provider; - -import java.net.URI; -import java.util.Collection; -import java.util.Collections; -import java.util.Set; - -import org.opendaylight.controller.sal.common.util.Rpcs; -import org.opendaylight.controller.sal.core.api.AbstractProvider; -import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; -import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration; -import org.opendaylight.controller.sal.core.api.RpcImplementation; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.osgi.framework.BundleContext; - -public class ExampleProvider extends AbstractProvider implements RpcImplementation { - - private final URI namespace = URI.create("http://cisco.com/example"); - private final QName QNAME = new QName(namespace,"heartbeat"); - private RpcRegistration reg; - - - @Override - public void onSessionInitiated(ProviderSession session) { - //Adding heartbeat 10 times just to make sure subscriber get it - for (int i=0;i<10;i++){ - System.out.println("ExampleProvider: Adding " + QNAME + " " + i); - reg = session.addRpcImplementation(QNAME, this); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - } - } - } - - @Override - public Set getSupportedRpcs() { - return Collections.singleton(QNAME); - } - - @Override - public RpcResult invokeRpc(QName rpc, CompositeNode input) { - if(QNAME.equals(rpc)) { - RpcResult output = Rpcs.getRpcResult(true, null, Collections.emptySet()); - return output; - } - RpcResult output = Rpcs.getRpcResult(false, null, Collections.emptySet()); - return output; - } - - @Override - protected void stopImpl(BundleContext context) { - if(reg != null) { - try { - reg.close(); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - -} diff --git a/opendaylight/md-sal/zeromq-routingtable/implementation/pom.xml b/opendaylight/md-sal/zeromq-routingtable/implementation/pom.xml index 37c973e864..2926786849 100644 --- a/opendaylight/md-sal/zeromq-routingtable/implementation/pom.xml +++ b/opendaylight/md-sal/zeromq-routingtable/implementation/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.opendaylight.controller @@ -28,7 +28,10 @@ true - + + org.opendaylight.controller.sal.connector.remoterpc.api, + org.opendaylight.controller.sal.connector.remoterpc.impl + javax.xml.bind.annotation, org.opendaylight.controller.sal.core, @@ -67,7 +70,12 @@ org.opendaylight.controller sal - 0.5.1-SNAPSHOT + + + org.osgi + org.osgi.compendium + + org.opendaylight.controller diff --git a/opendaylight/netconf/config-netconf-connector/pom.xml b/opendaylight/netconf/config-netconf-connector/pom.xml index d855f15541..888f13c8fe 100644 --- a/opendaylight/netconf/config-netconf-connector/pom.xml +++ b/opendaylight/netconf/config-netconf-connector/pom.xml @@ -136,7 +136,9 @@ org.osgi.framework, org.osgi.util.tracker, org.slf4j, - org.w3c.dom + org.w3c.dom, + com.google.common.io, + org.opendaylight.yangtools.yang.model.api.type diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java index 4b6dcfd465..697b811d51 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java @@ -12,7 +12,9 @@ import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIf import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; +import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition; import javax.management.openmbean.ArrayType; import javax.management.openmbean.CompositeType; @@ -29,7 +31,10 @@ public abstract class AttributeIfcSwitchStatement { if (attributeIfc instanceof JavaAttribute) { try { - return caseJavaAttribute(attributeIfc.getOpenType()); + if(((JavaAttribute)attributeIfc).getTypeDefinition() instanceof BinaryTypeDefinition) { + return caseJavaBinaryAttribute(attributeIfc.getOpenType()); + } else + return caseJavaAttribute(attributeIfc.getOpenType()); } catch (UnknownOpenTypeException e) { throw getIllegalArgumentException(attributeIfc); } @@ -37,7 +42,9 @@ public abstract class AttributeIfcSwitchStatement { } else if (attributeIfc instanceof DependencyAttribute) { return caseDependencyAttribute(((DependencyAttribute) attributeIfc).getOpenType()); } else if (attributeIfc instanceof ListAttribute) { - return caseListAttribute(((ListAttribute) attributeIfc).getOpenType()); + return caseListAttribute((ArrayType) attributeIfc.getOpenType()); + } else if (attributeIfc instanceof ListDependenciesAttribute) { + return caseListDependeciesAttribute((ArrayType) attributeIfc.getOpenType()); } else if (attributeIfc instanceof TOAttribute) { return caseTOAttribute(((TOAttribute) attributeIfc).getOpenType()); } @@ -45,6 +52,10 @@ public abstract class AttributeIfcSwitchStatement { throw getIllegalArgumentException(attributeIfc); } + protected T caseJavaBinaryAttribute(OpenType openType) { + return caseJavaAttribute(openType); + } + private IllegalArgumentException getIllegalArgumentException(AttributeIfc attributeIfc) { return new IllegalArgumentException("Unknown attribute type " + attributeIfc.getClass() + ", " + attributeIfc + " with open type:" + attributeIfc.getOpenType()); @@ -74,6 +85,7 @@ public abstract class AttributeIfcSwitchStatement { protected abstract T caseListAttribute(ArrayType openType); + protected abstract T caseListDependeciesAttribute(ArrayType openType); private static class UnknownOpenTypeException extends RuntimeException { public UnknownOpenTypeException(String message) { diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java index 867d94e0b7..7939112628 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java @@ -27,11 +27,14 @@ public abstract class AbstractAttributeReadingStrategy implements AttributeReadi @Override public AttributeConfigElement readElement(List configNodes) { if (configNodes.size() == 0) - return AttributeConfigElement.createNullValue(nullableDefault); + return AttributeConfigElement.createNullValue(postprocessNullableDefault(nullableDefault)); return readElementHook(configNodes); } abstract AttributeConfigElement readElementHook(List configNodes); + protected Object postprocessNullableDefault(String nullableDefault) { + return nullableDefault; + } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java index 598935a0bc..a1f46dde54 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java @@ -9,7 +9,6 @@ package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; import com.google.common.base.Optional; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy; import javax.management.openmbean.OpenType; @@ -47,24 +46,14 @@ public class AttributeConfigElement { } - public static AttributeConfigElement create(AttributeIfc attributeIfc, Object value) { - String nullableDefault = attributeIfc.getNullableDefault(); - return create(nullableDefault, value); - } - - public static AttributeConfigElement create(String nullableDefault, Object value) { + public static AttributeConfigElement create(Object nullableDefault, Object value) { return new AttributeConfigElement(nullableDefault, value); } - public static AttributeConfigElement createNullValue(AttributeIfc attributeIfc) { - return new AttributeConfigElement(attributeIfc.getNullableDefault(), null); - } - - public static AttributeConfigElement createNullValue(String nullableDefault) { + public static AttributeConfigElement createNullValue(Object nullableDefault) { return new AttributeConfigElement(nullableDefault, null); } - public Object getValue() { return value; } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java index bc3c74a88f..a2691f241c 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java @@ -12,11 +12,13 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; import javax.management.openmbean.ArrayType; import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; import java.util.Map; import java.util.Map.Entry; @@ -40,6 +42,11 @@ public class ObjectXmlReader extends AttributeIfcSwitchStatement openType) { + return new SimpleBinaryAttributeReadingStrategy(lastAttribute.getNullableDefault()); + } + @Override public AttributeReadingStrategy caseJavaSimpleAttribute(SimpleType openType) { return new SimpleAttributeReadingStrategy(lastAttribute.getNullableDefault()); @@ -86,4 +93,11 @@ public class ObjectXmlReader extends AttributeIfcSwitchStatement openType) { + Preconditions.checkState(lastAttribute instanceof ListDependenciesAttribute); + AttributeReadingStrategy innerStrategy = caseDependencyAttribute(SimpleType.OBJECTNAME); + return new ArrayAttributeReadingStrategy(lastAttribute.getNullableDefault(), innerStrategy); + } + } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java index be86a2ab6f..a8605243af 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java @@ -28,7 +28,13 @@ public class SimpleAttributeReadingStrategy extends AbstractAttributeReadingStra String textContent = xmlElement.getTextContent(); Preconditions.checkNotNull(textContent, "This element should contain text %s", xmlElement); - return AttributeConfigElement.create(getNullableDefault(), postprocessParsedValue(textContent)); + return AttributeConfigElement.create(postprocessNullableDefault(getNullableDefault()), + postprocessParsedValue(textContent)); + } + + @Override + protected Object postprocessNullableDefault(String nullableDefault) { + return nullableDefault; } protected Object postprocessParsedValue(String textContent) { diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleBinaryAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleBinaryAttributeReadingStrategy.java new file mode 100644 index 0000000000..2cac9029c6 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleBinaryAttributeReadingStrategy.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; + +import com.google.common.collect.Lists; +import com.google.common.io.BaseEncoding; + +import java.util.List; + +public class SimpleBinaryAttributeReadingStrategy extends SimpleAttributeReadingStrategy { + + public SimpleBinaryAttributeReadingStrategy(String nullableDefault) { + super(nullableDefault); + } + + protected Object postprocessParsedValue(String textContent) { + BaseEncoding en = BaseEncoding.base64(); + byte[] decode = en.decode(textContent); + List parsed = Lists.newArrayListWithCapacity(decode.length); + for (byte b : decode) { + parsed.add(Byte.toString(b)); + } + return parsed; + } + + @Override + protected Object postprocessNullableDefault(String nullableDefault) { + return nullableDefault == null ? null : postprocessParsedValue(nullableDefault); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleCompositeAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleCompositeAttributeReadingStrategy.java index 9249ac9fa8..22c9e015a9 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleCompositeAttributeReadingStrategy.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleCompositeAttributeReadingStrategy.java @@ -14,7 +14,6 @@ import java.util.HashMap; public class SimpleCompositeAttributeReadingStrategy extends SimpleAttributeReadingStrategy { - private final String key; public SimpleCompositeAttributeReadingStrategy(String nullableDefault, String key) { @@ -28,4 +27,8 @@ public class SimpleCompositeAttributeReadingStrategy extends SimpleAttributeRead return map; } + @Override + protected Object postprocessNullableDefault(String nullableDefault) { + return nullableDefault == null ? null : postprocessParsedValue(nullableDefault); + } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java index 853197c0b0..6e5bd0d3fe 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java @@ -13,6 +13,7 @@ import com.google.common.collect.Maps; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services; @@ -121,4 +122,10 @@ public class ObjectMapper extends AttributeIfcSwitchStatement> caseListDependeciesAttribute(ArrayType openType) { + Preconditions.checkState(lastAttribute instanceof ListDependenciesAttribute); + return new ArrayAttributeMappingStrategy(openType, caseDependencyAttribute(SimpleType.OBJECTNAME)); + } + } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java index a85f3064cf..c477821051 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java @@ -70,7 +70,7 @@ final class CompositeAttributeResolvingStrategy extends parsedInnerValue.isPresent() ? parsedInnerValue.get() : null); } - CompositeDataSupport parsedValue = null; + CompositeDataSupport parsedValue; try { parsedValue = new CompositeDataSupport(getOpenType(), items); } catch (OpenDataException e) { diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java index c321164cf6..f5a2511260 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java @@ -12,6 +12,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services; @@ -109,4 +110,10 @@ public class ObjectResolver extends AttributeIfcSwitchStatement> caseListDependeciesAttribute(ArrayType openType) { + Preconditions.checkState(lastAttribute instanceof ListDependenciesAttribute); + return new ArrayAttributeResolvingStrategy(caseDependencyAttribute(SimpleType.OBJECTNAME), openType); + } + } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java index 99e969970c..a174e9a251 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java @@ -12,12 +12,14 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; import org.w3c.dom.Document; import javax.management.openmbean.ArrayType; import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; import java.util.Map; import java.util.Map.Entry; @@ -50,6 +52,11 @@ public class ObjectXmlWriter extends AttributeIfcSwitchStatement openType) { + return new SimpleBinaryAttributeWritingStrategy(document, key); + } + @Override protected AttributeWritingStrategy caseJavaSimpleAttribute(SimpleType openType) { return new SimpleAttributeWritingStrategy(document, key); @@ -96,4 +103,11 @@ public class ObjectXmlWriter extends AttributeIfcSwitchStatement openType) { + Preconditions.checkState(lastAttribute instanceof ListDependenciesAttribute); + AttributeWritingStrategy innerStrategy = caseDependencyAttribute(SimpleType.OBJECTNAME); + return new ArrayAttributeWritingStrategy(innerStrategy); + } + } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleBinaryAttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleBinaryAttributeWritingStrategy.java new file mode 100644 index 0000000000..c159e46987 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleBinaryAttributeWritingStrategy.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml; + +import com.google.common.base.Preconditions; +import com.google.common.io.BaseEncoding; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.w3c.dom.Document; + +import java.util.List; + +public class SimpleBinaryAttributeWritingStrategy extends SimpleAttributeWritingStrategy { + + /** + * @param document + * @param key + */ + public SimpleBinaryAttributeWritingStrategy(Document document, String key) { + super(document, key); + } + + protected Object preprocess(Object value) { + Util.checkType(value, List.class); + BaseEncoding en = BaseEncoding.base64(); + + List list = (List) value; + byte[] decoded = new byte[list.size()]; + int i = 0; + for (Object bAsStr : list) { + Preconditions.checkArgument(bAsStr instanceof String, "Unexpected inner value for %s, expected string", value); + byte b = Byte.parseByte((String) bAsStr); + decoded[i++] = b; + } + + return en.encode(decoded); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java index e4fa6f504b..f522668733 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java @@ -284,8 +284,13 @@ public final class Services { public static ServiceInstance fromString(String instanceId) { instanceId = instanceId.trim(); Matcher matcher = p.matcher(instanceId); + if(matcher.matches() == false) { + matcher = pDeprecated.matcher(instanceId); + } + Preconditions.checkArgument(matcher.matches(), "Unexpected format for provider, expected " + p.toString() - + " but was " + instanceId); + + " or " + pDeprecated.toString() + " but was " + instanceId); + String factoryName = matcher.group(1); String instanceName = matcher.group(2); return new ServiceInstance(factoryName, instanceName); @@ -310,16 +315,25 @@ public final class Services { return instanceName; } - private static final String blueprint = "/" + XmlNetconfConstants.CONFIG_KEY + "/" + private static final String blueprint = "/" + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "[" - + XmlNetconfConstants.NAME_KEY + "='%s']/" + XmlNetconfConstants.INSTANCE_KEY + "[" + + XmlNetconfConstants.TYPE_KEY + "='%s'][" + XmlNetconfConstants.NAME_KEY + "='%s']"; - private static final String blueprintR = "/" + XmlNetconfConstants.CONFIG_KEY + "/" + // TODO unify with xpath in RuntimeRpc + + // Previous version of xpath, needs to be supported for backwards compatibility (persisted configs by config-persister) + private static final String blueprintRDeprecated = "/" + XmlNetconfConstants.CONFIG_KEY + "/" + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\[" + XmlNetconfConstants.NAME_KEY + "='%s'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\[" + XmlNetconfConstants.NAME_KEY + "='%s'\\]"; + private static final String blueprintR = "/" + + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\[" + + XmlNetconfConstants.TYPE_KEY + "='%s'\\]\\[" + + XmlNetconfConstants.NAME_KEY + "='%s'\\]"; + + private static final Pattern pDeprecated = Pattern.compile(String.format(blueprintRDeprecated, "(.+)", "(.+)")); private static final Pattern p = Pattern.compile(String.format(blueprintR, "(.+)", "(.+)")); @Override diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java index d91e38db33..f8916ecac2 100644 --- a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java @@ -55,7 +55,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import javax.management.InstanceAlreadyExistsException; @@ -125,6 +124,7 @@ public class NetconfMappingTest extends AbstractConfigTest { edit("netconfMessages/editConfig.xml"); checkBinaryLeafEdited(getConfigCandidate()); + // default-operation:none, should not affect binary leaf edit("netconfMessages/editConfig_none.xml"); checkBinaryLeafEdited(getConfigCandidate()); @@ -136,6 +136,7 @@ public class NetconfMappingTest extends AbstractConfigTest { checkBinaryLeafEdited(response); checkTypeConfigAttribute(response); checkTypedefs(response); + checkTestingDeps(response); checkEnum(response); checkBigDecimal(response); @@ -164,8 +165,12 @@ public class NetconfMappingTest extends AbstractConfigTest { } private void checkBigDecimal(Element response) { - int size = response.getElementsByTagName("sleep-factor").getLength(); - assertEquals(1, size); + String responseTrimmed = XmlUtil.toString(response).replaceAll("\\s", ""); + + assertContainsString(responseTrimmed, "2.58"); + // Default + assertContainsString(responseTrimmed, "2.00"); + } private void closeSession() throws NetconfDocumentedException, ParserConfigurationException, SAXException, @@ -235,14 +240,9 @@ public class NetconfMappingTest extends AbstractConfigTest { edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml"); } catch (NetconfDocumentedException e) { String message = e.getMessage(); - assertThat(message, - JUnitMatchers - .containsString("Element simple-long-2 present multiple times with different namespaces")); - assertThat(message, - JUnitMatchers.containsString("urn:opendaylight:params:xml:ns:yang:controller:test:impl")); - assertThat(message, - JUnitMatchers - .containsString(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG)); + assertContainsString(message, "Element simple-long-2 present multiple times with different namespaces"); + assertContainsString(message, "urn:opendaylight:params:xml:ns:yang:controller:test:impl"); + assertContainsString(message, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); throw e; } } @@ -253,9 +253,9 @@ public class NetconfMappingTest extends AbstractConfigTest { edit("netconfMessages/namespaces/editConfig_differentNamespaceTO.xml"); } catch (NetconfDocumentedException e) { String message = e.getMessage(); - assertThat(message, JUnitMatchers.containsString("Unrecognised elements")); - assertThat(message, JUnitMatchers.containsString("simple-int2")); - assertThat(message, JUnitMatchers.containsString("dto_d")); + assertContainsString(message, "Unrecognised elements"); + assertContainsString(message, "simple-int2"); + assertContainsString(message, "dto_d"); throw e; } } @@ -266,13 +266,9 @@ public class NetconfMappingTest extends AbstractConfigTest { edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml"); } catch (NetconfDocumentedException e) { String message = e.getMessage(); - assertThat(message, - JUnitMatchers.containsString("Element binaryLeaf present multiple times with different namespaces")); - assertThat(message, - JUnitMatchers.containsString("urn:opendaylight:params:xml:ns:yang:controller:test:impl")); - assertThat(message, - JUnitMatchers - .containsString(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG)); + assertContainsString(message, "Element binaryLeaf present multiple times with different namespaces"); + assertContainsString(message, "urn:opendaylight:params:xml:ns:yang:controller:test:impl"); + assertContainsString(message, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); throw e; } } @@ -309,8 +305,8 @@ public class NetconfMappingTest extends AbstractConfigTest { try { edit(file); } catch (NetconfDocumentedException e) { - assertThat(e.getMessage(), JUnitMatchers.containsString("Unrecognised elements")); - assertThat(e.getMessage(), JUnitMatchers.containsString("unknownAttribute")); + assertContainsString(e.getMessage(), "Unrecognised elements"); + assertContainsString(e.getMessage(), "unknownAttribute"); continue; } fail("Unrecognised test should throw exception " + file); @@ -356,22 +352,37 @@ public class NetconfMappingTest extends AbstractConfigTest { } private void checkBinaryLeafEdited(final Element response) { - final NodeList children = response.getElementsByTagName("binaryLeaf"); - assertEquals(3, children.getLength()); - final StringBuffer buf = new StringBuffer(); - for (int i = 0; i < 3; i++) { - final Element e = (Element) children.item(i); - buf.append(XmlElement.fromDomElement(e).getTextContent()); - } - assertEquals("810", buf.toString()); + String responseTrimmed = XmlUtil.toString(response).replaceAll("\\s", ""); + String substring = "YmluYXJ5"; + assertContainsString(responseTrimmed, substring); + substring = "ZGVmYXVsdEJpbg=="; + assertContainsString(responseTrimmed, substring); } private void checkTypedefs(final Element response) { - NodeList children = response.getElementsByTagName("extended"); - assertEquals(1, children.getLength()); + String responseTrimmed = XmlUtil.toString(response).replaceAll("\\s", ""); + + String substring = "10"; + assertContainsString(responseTrimmed, substring); + // Default + assertContainsString(responseTrimmed, + "1"); - children = response.getElementsByTagName("extended-twice"); - assertEquals(1, children.getLength()); + assertContainsString(responseTrimmed, + "20"); + // Default + assertContainsString(responseTrimmed, + "2"); + + assertContainsString(responseTrimmed, + "TWO"); + // Default + assertContainsString(responseTrimmed, + "ONE"); + } + + private void assertContainsString(String string, String substring) { + assertThat(string, JUnitMatchers.containsString(substring)); } private void checkEnum(final Element response) { @@ -394,6 +405,11 @@ public class NetconfMappingTest extends AbstractConfigTest { fail("Enum attribute " + enumName + ":" + enumContent + " not present in " + XmlUtil.toString(response)); } + private void checkTestingDeps(Element response) { + int testingDepsSize = response.getElementsByTagName("testing-deps").getLength(); + assertEquals(2, testingDepsSize); + } + private void checkTypeConfigAttribute(Element response) { XmlElement modulesElement = XmlElement.fromDomElement(response).getOnlyChildElement("data") @@ -450,17 +466,17 @@ public class NetconfMappingTest extends AbstractConfigTest { RuntimeRpc netconf = new RuntimeRpc(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID); response = executeOp(netconf, "netconfMessages/rpc.xml"); - assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("testarg1".toUpperCase())); + assertContainsString(XmlUtil.toString(response), "testarg1".toUpperCase()); response = executeOp(netconf, "netconfMessages/rpcInner.xml"); - assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("ok")); + assertContainsString(XmlUtil.toString(response), "ok"); response = executeOp(netconf, "netconfMessages/rpcInnerInner.xml"); - assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("true")); + assertContainsString(XmlUtil.toString(response), "true"); response = executeOp(netconf, "netconfMessages/rpcInnerInner_complex_output.xml"); - assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("1")); - assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("2")); + assertContainsString(XmlUtil.toString(response), "1"); + assertContainsString(XmlUtil.toString(response), "2"); } private Element get() throws NetconfDocumentedException, ParserConfigurationException, SAXException, IOException { diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java index 99b7ee60a2..1c3ac7a455 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java @@ -8,45 +8,22 @@ package org.opendaylight.controller.netconf.persist.impl; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.Sets; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; -import javax.annotation.concurrent.ThreadSafe; -import javax.management.InstanceNotFoundException; -import javax.management.MBeanServerConnection; -import javax.management.Notification; -import javax.management.NotificationListener; -import javax.management.ObjectName; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpression; -import org.opendaylight.controller.config.api.ConflictingVersionException; -import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; -import org.opendaylight.controller.config.persist.api.Persister; -import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification; import org.opendaylight.controller.netconf.api.jmx.DefaultCommitOperationMXBean; import org.opendaylight.controller.netconf.api.jmx.NetconfJMXNotification; import org.opendaylight.controller.netconf.client.NetconfClient; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; -import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil; -import org.opendaylight.controller.netconf.util.xml.XmlElement; -import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.xml.sax.SAXException; + +import javax.annotation.concurrent.ThreadSafe; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanServerConnection; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import java.io.Closeable; +import java.io.IOException; +import java.util.regex.Pattern; /** * Responsible for listening for notifications from netconf containing latest @@ -57,163 +34,27 @@ import org.xml.sax.SAXException; public class ConfigPersisterNotificationHandler implements NotificationListener, Closeable { private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class); - private static final int NETCONF_SEND_ATTEMPT_MS_DELAY = 1000; - private static final int NETCONF_SEND_ATTEMPTS = 20; - - private final InetSocketAddress address; - private final EventLoopGroup nettyThreadgroup; - - private NetconfClientDispatcher netconfClientDispatcher; - private NetconfClient netconfClient; - - private final Persister persister; - private final MBeanServerConnection mbeanServer; - - - private final ObjectName on = DefaultCommitOperationMXBean.objectName; - - public static final long DEFAULT_TIMEOUT = 120000L;// 120 seconds until netconf must be stable - private final long timeout; + private final MBeanServerConnection mBeanServerConnection; + private final NetconfClient netconfClient; + private final PersisterAggregator persisterAggregator; private final Pattern ignoredMissingCapabilityRegex; - public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address, - MBeanServerConnection mbeanServer, Pattern ignoredMissingCapabilityRegex) { - this(persister, address, mbeanServer, DEFAULT_TIMEOUT, ignoredMissingCapabilityRegex); - - } - - public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address, - MBeanServerConnection mbeanServer, long timeout, Pattern ignoredMissingCapabilityRegex) { - this.persister = persister; - this.address = address; - this.mbeanServer = mbeanServer; - this.timeout = timeout; - - this.nettyThreadgroup = new NioEventLoopGroup(); + public ConfigPersisterNotificationHandler(MBeanServerConnection mBeanServerConnection, NetconfClient netconfClient, + PersisterAggregator persisterAggregator, Pattern ignoredMissingCapabilityRegex) { + this.mBeanServerConnection = mBeanServerConnection; + this.netconfClient = netconfClient; + this.persisterAggregator = persisterAggregator; this.ignoredMissingCapabilityRegex = ignoredMissingCapabilityRegex; } - public void init() throws InterruptedException { - Optional maybeConfig = loadLastConfig(); - - if (maybeConfig.isPresent()) { - logger.debug("Last config found {}", persister); - ConflictingVersionException lastException = null; - pushLastConfigWithRetries(maybeConfig, lastException); - - } else { - // this ensures that netconf is initialized, this is first - // connection - // this means we can register as listener for commit - registerToNetconf(Collections.emptySet()); - - logger.info("No last config provided by backend storage {}", persister); - } + public void init() { registerAsJMXListener(); } - private void pushLastConfigWithRetries(Optional maybeConfig, ConflictingVersionException lastException) throws InterruptedException { - int maxAttempts = 30; - for(int i = 0 ; i < maxAttempts; i++) { - registerToNetconf(maybeConfig.get().getCapabilities()); - - final String configSnapshot = maybeConfig.get().getConfigSnapshot(); - logger.trace("Pushing following xml to netconf {}", configSnapshot); - try { - pushLastConfig(XmlUtil.readXmlToElement(configSnapshot)); - return; - } catch(ConflictingVersionException e) { - closeClientAndDispatcher(netconfClient, netconfClientDispatcher); - lastException = e; - Thread.sleep(1000); - } catch (SAXException | IOException e) { - throw new IllegalStateException("Unable to load last config", e); - } - } - throw new IllegalStateException("Failed to push configuration, maximum attempt count has been reached: " - + maxAttempts, lastException); - } - - private synchronized long registerToNetconf(Set expectedCaps) throws InterruptedException { - - Set currentCapabilities = Sets.newHashSet(); - - // TODO think about moving capability subset check to netconf client - // could be utilized by integration tests - - long pollingStart = System.currentTimeMillis(); - int delay = 5000; - - int attempt = 0; - - long deadline = pollingStart + timeout; - while (System.currentTimeMillis() < deadline) { - attempt++; - netconfClientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup); - try { - netconfClient = new NetconfClient(this.toString(), address, delay, netconfClientDispatcher); - } catch (IllegalStateException e) { - logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e); - netconfClientDispatcher.close(); - Thread.sleep(delay); - continue; - } - currentCapabilities = netconfClient.getCapabilities(); - - if (isSubset(currentCapabilities, expectedCaps)) { - logger.debug("Hello from netconf stable with {} capabilities", currentCapabilities); - long currentSessionId = netconfClient.getSessionId(); - logger.info("Session id received from netconf server: {}", currentSessionId); - return currentSessionId; - } - - - - logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, currentCapabilities); - - closeClientAndDispatcher(netconfClient, netconfClientDispatcher); - - Thread.sleep(delay); - } - Set allNotFound = new HashSet<>(expectedCaps); - allNotFound.removeAll(currentCapabilities); - logger.error("Netconf server did not provide required capabilities. Expected but not found: {}, all expected {}, current {}", - allNotFound, expectedCaps ,currentCapabilities); - throw new RuntimeException("Netconf server did not provide required capabilities. Expected but not found:" + allNotFound); - - } - - private static void closeClientAndDispatcher(Closeable client, Closeable dispatcher) { - Exception fromClient = null; - try { - client.close(); - } catch (Exception e) { - fromClient = e; - } finally { - try { - dispatcher.close(); - } catch (Exception e) { - if (fromClient != null) { - e.addSuppressed(fromClient); - } - - throw new RuntimeException("Error closing temporary client ", e); - } - } - } - - private boolean isSubset(Set currentCapabilities, Set expectedCaps) { - for (String exCap : expectedCaps) { - if (currentCapabilities.contains(exCap) == false) - return false; - } - return true; - } - private void registerAsJMXListener() { logger.trace("Called registerAsJMXListener"); try { - mbeanServer.addNotificationListener(on, this, null, null); + mBeanServerConnection.addNotificationListener(DefaultCommitOperationMXBean.objectName, this, null, null); } catch (InstanceNotFoundException | IOException e) { throw new RuntimeException("Cannot register as JMX listener to netconf", e); } @@ -242,7 +83,7 @@ public class ConfigPersisterNotificationHandler implements NotificationListener, private void handleAfterCommitNotification(final CommitJMXNotification notification) { try { - persister.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(), + persisterAggregator.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(), notification.getCapabilities(), ignoredMissingCapabilityRegex)); logger.info("Configuration persisted successfully"); } catch (IOException e) { @@ -250,137 +91,21 @@ public class ConfigPersisterNotificationHandler implements NotificationListener, } } - private Optional loadLastConfig() { - Optional maybeConfigElement; - try { - maybeConfigElement = persister.loadLastConfig(); - } catch (IOException e) { - throw new RuntimeException("Unable to load configuration", e); - } - return maybeConfigElement; - } - - private synchronized void pushLastConfig(Element xmlToBePersisted) throws ConflictingVersionException { - logger.info("Pushing last configuration to netconf"); - StringBuilder response = new StringBuilder("editConfig response = {"); - - - NetconfMessage message = createEditConfigMessage(xmlToBePersisted, "/netconfOp/editConfig.xml"); - - // sending message to netconf - NetconfMessage responseMessage = netconfClient.sendMessage(message, NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY); - - XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument()); - Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY)); - element = element.getOnlyChildElement(); - - checkIsOk(element, responseMessage); - response.append(XmlUtil.toString(responseMessage.getDocument())); - response.append("}"); - responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml"), NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY); - - element = XmlElement.fromDomDocument(responseMessage.getDocument()); - Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY)); - element = element.getOnlyChildElement(); - - checkIsOk(element, responseMessage); - response.append("commit response = {"); - response.append(XmlUtil.toString(responseMessage.getDocument())); - response.append("}"); - logger.info("Last configuration loaded successfully"); - logger.trace("Detailed message {}", response); - } - - static void checkIsOk(XmlElement element, NetconfMessage responseMessage) throws ConflictingVersionException { - if (element.getName().equals(XmlNetconfConstants.OK)) { - return; - } - - if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) { - logger.warn("Can not load last configuration, operation failed"); - // is it ConflictingVersionException ? - XPathExpression xPathExpression = XMLNetconfUtil.compileXPath("/netconf:rpc-reply/netconf:rpc-error/netconf:error-info/netconf:error"); - String error = (String) XmlUtil.evaluateXPath(xPathExpression, element.getDomElement(), XPathConstants.STRING); - if (error!=null && error.contains(ConflictingVersionException.class.getCanonicalName())) { - throw new ConflictingVersionException(error); - } - throw new IllegalStateException("Can not load last configuration, operation failed: " - + XmlUtil.toString(responseMessage.getDocument())); - } - - logger.warn("Can not load last configuration. Operation failed."); - throw new IllegalStateException("Can not load last configuration. Operation failed: " - + XmlUtil.toString(responseMessage.getDocument())); - } - - private static NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) { - try (InputStream stream = ConfigPersisterNotificationHandler.class.getResourceAsStream(editConfigResourcename)) { - Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcename); - - Document doc = XmlUtil.readXmlToDocument(stream); - - doc.getDocumentElement(); - XmlElement editConfigElement = XmlElement.fromDomDocument(doc).getOnlyChildElement(); - XmlElement configWrapper = editConfigElement.getOnlyChildElement(XmlNetconfConstants.CONFIG_KEY); - editConfigElement.getDomElement().removeChild(configWrapper.getDomElement()); - for (XmlElement el : XmlElement.fromDomElement(dataElement).getChildElements()) { - configWrapper.appendChild((Element) doc.importNode(el.getDomElement(), true)); - } - editConfigElement.appendChild(configWrapper.getDomElement()); - return new NetconfMessage(doc); - } catch (IOException | SAXException e) { - throw new RuntimeException("Unable to parse message from resources " + editConfigResourcename, e); - } - } - - private NetconfMessage getNetconfMessageFromResource(String resource) { - try (InputStream stream = getClass().getResourceAsStream(resource)) { - Preconditions.checkNotNull(stream, "Unable to load resource " + resource); - return new NetconfMessage(XmlUtil.readXmlToDocument(stream)); - } catch (SAXException | IOException e) { - throw new RuntimeException("Unable to parse message from resources " + resource, e); - } - } - @Override public synchronized void close() { - // TODO persister is received from constructor, should not be closed - // here - try { - persister.close(); - } catch (Exception e) { - logger.warn("Unable to close config persister {}", persister, e); - } - - if (netconfClient != null) { - try { - netconfClient.close(); - } catch (Exception e) { - logger.warn("Unable to close connection to netconf {}", netconfClient, e); - } - } - - if (netconfClientDispatcher != null) { - try { - netconfClientDispatcher.close(); - } catch (Exception e) { - logger.warn("Unable to close connection to netconf {}", netconfClientDispatcher, e); - } - } - - try { - nettyThreadgroup.shutdownGracefully(); - } catch (Exception e) { - logger.warn("Unable to close netconf client thread group {}", netconfClientDispatcher, e); - } - // unregister from JMX + ObjectName on = DefaultCommitOperationMXBean.objectName; try { - if (mbeanServer.isRegistered(on)) { - mbeanServer.removeNotificationListener(on, this); + if (mBeanServerConnection.isRegistered(on)) { + mBeanServerConnection.removeNotificationListener(on, this); } } catch (Exception e) { logger.warn("Unable to unregister {} as listener for {}", this, on, e); } } + + public NetconfClient getNetconfClient() { + return netconfClient; + } + } 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/ConfigPusher.java new file mode 100644 index 0000000000..044346e2c5 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.persist.impl; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import io.netty.channel.EventLoopGroup; +import org.opendaylight.controller.config.api.ConflictingVersionException; +import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.client.NetconfClient; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import javax.annotation.concurrent.Immutable; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Immutable +public class ConfigPusher { + private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class); + private static final int NETCONF_SEND_ATTEMPT_MS_DELAY = 1000; + private static final int NETCONF_SEND_ATTEMPTS = 20; + + private final InetSocketAddress address; + private final EventLoopGroup nettyThreadgroup; + + + public static final long DEFAULT_TIMEOUT = 120000L;// 120 seconds until netconf must be stable + private final long timeout; + + public ConfigPusher(InetSocketAddress address, EventLoopGroup nettyThreadgroup) { + this(address, DEFAULT_TIMEOUT, nettyThreadgroup); + + } + + public ConfigPusher(InetSocketAddress address, long timeout, EventLoopGroup nettyThreadgroup) { + this.address = address; + this.timeout = timeout; + + this.nettyThreadgroup = nettyThreadgroup; + } + + public synchronized NetconfClient init(List configs) throws InterruptedException { + logger.debug("Last config snapshots to be pushed to netconf: {}", configs); + return pushAllConfigs(configs); + } + + private synchronized NetconfClient pushAllConfigs(List configs) throws InterruptedException { + NetconfClient netconfClient = makeNetconfConnection(Collections.emptySet(), Optional.absent()); + for (ConfigSnapshotHolder configSnapshotHolder: configs){ + netconfClient = pushSnapshotWithRetries(configSnapshotHolder, Optional.of(netconfClient)); + } + return netconfClient; + } + + private synchronized NetconfClient pushSnapshotWithRetries(ConfigSnapshotHolder configSnapshotHolder, + Optional oldClientForPossibleReuse) + throws InterruptedException { + + ConflictingVersionException lastException = null; + int maxAttempts = 30; + for(int i = 0 ; i < maxAttempts; i++) { + NetconfClient netconfClient = makeNetconfConnection(configSnapshotHolder.getCapabilities(), oldClientForPossibleReuse); + final String configSnapshot = configSnapshotHolder.getConfigSnapshot(); + logger.trace("Pushing following xml to netconf {}", configSnapshot); + try { + pushLastConfig(configSnapshotHolder, netconfClient); + return netconfClient; + } catch(ConflictingVersionException e) { + Util.closeClientAndDispatcher(netconfClient); + lastException = e; + Thread.sleep(1000); + } catch (SAXException | IOException e) { + throw new IllegalStateException("Unable to load last config", e); + } + } + throw new IllegalStateException("Failed to push configuration, maximum attempt count has been reached: " + + maxAttempts, lastException); + } + + /** + * @param expectedCaps capabilities that server hello must contain. Will retry until all are found or throws RuntimeException. + * If empty set is provided, will only make sure netconf client successfuly connected to the server. + * @param oldClientForPossibleReuse if present, try to get expected capabilities from it before closing it and retrying with + * new client connection. + * @return NetconfClient that has all required capabilities from server. + */ + private synchronized NetconfClient makeNetconfConnection(Set expectedCaps, + Optional oldClientForPossibleReuse) + throws InterruptedException { + + if (oldClientForPossibleReuse.isPresent()) { + NetconfClient oldClient = oldClientForPossibleReuse.get(); + if (Util.isSubset(oldClient, expectedCaps)) { + return oldClient; + } else { + Util.closeClientAndDispatcher(oldClient); + } + } + + // TODO think about moving capability subset check to netconf client + // could be utilized by integration tests + + long pollingStart = System.currentTimeMillis(); + int delay = 5000; + + int attempt = 0; + + long deadline = pollingStart + timeout; + + Set latestCapabilities = new HashSet<>(); + while (System.currentTimeMillis() < deadline) { + attempt++; + NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup); + NetconfClient netconfClient; + try { + netconfClient = new NetconfClient(this.toString(), address, delay, netconfClientDispatcher); + } catch (IllegalStateException e) { + logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e); + netconfClientDispatcher.close(); + Thread.sleep(delay); + continue; + } + latestCapabilities = netconfClient.getCapabilities(); + if (Util.isSubset(netconfClient, expectedCaps)) { + logger.debug("Hello from netconf stable with {} capabilities", latestCapabilities); + logger.info("Session id received from netconf server: {}", netconfClient.getClientSession()); + return netconfClient; + } + logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, latestCapabilities); + Util.closeClientAndDispatcher(netconfClient); + Thread.sleep(delay); + } + Set allNotFound = new HashSet<>(expectedCaps); + allNotFound.removeAll(latestCapabilities); + logger.error("Netconf server did not provide required capabilities. Expected but not found: {}, all expected {}, current {}", + allNotFound, expectedCaps, latestCapabilities); + throw new RuntimeException("Netconf server did not provide required capabilities. Expected but not found:" + allNotFound); + } + + + private synchronized void pushLastConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfClient netconfClient) + throws ConflictingVersionException, IOException, SAXException { + + Element xmlToBePersisted = XmlUtil.readXmlToElement(configSnapshotHolder.getConfigSnapshot()); + logger.info("Pushing last configuration to netconf: {}", configSnapshotHolder); + StringBuilder response = new StringBuilder("editConfig response = {"); + + + NetconfMessage message = createEditConfigMessage(xmlToBePersisted, "/netconfOp/editConfig.xml"); + + // sending message to netconf + NetconfMessage responseMessage = netconfClient.sendMessage(message, NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY); + + XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument()); + Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY)); + element = element.getOnlyChildElement(); + + Util.checkIsOk(element, responseMessage); + response.append(XmlUtil.toString(responseMessage.getDocument())); + response.append("}"); + responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml"), NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY); + + element = XmlElement.fromDomDocument(responseMessage.getDocument()); + Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY)); + element = element.getOnlyChildElement(); + + Util.checkIsOk(element, responseMessage); + response.append("commit response = {"); + response.append(XmlUtil.toString(responseMessage.getDocument())); + response.append("}"); + logger.info("Last configuration loaded successfully"); + logger.trace("Detailed message {}", response); + } + + private static NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) { + try (InputStream stream = ConfigPersisterNotificationHandler.class.getResourceAsStream(editConfigResourcename)) { + Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcename); + + Document doc = XmlUtil.readXmlToDocument(stream); + + doc.getDocumentElement(); + XmlElement editConfigElement = XmlElement.fromDomDocument(doc).getOnlyChildElement(); + XmlElement configWrapper = editConfigElement.getOnlyChildElement(XmlNetconfConstants.CONFIG_KEY); + editConfigElement.getDomElement().removeChild(configWrapper.getDomElement()); + for (XmlElement el : XmlElement.fromDomElement(dataElement).getChildElements()) { + configWrapper.appendChild((Element) doc.importNode(el.getDomElement(), true)); + } + editConfigElement.appendChild(configWrapper.getDomElement()); + return new NetconfMessage(doc); + } catch (IOException | SAXException e) { + throw new RuntimeException("Unable to parse message from resources " + editConfigResourcename, e); + } + } + + private static NetconfMessage getNetconfMessageFromResource(String resource) { + try (InputStream stream = ConfigPusher.class.getResourceAsStream(resource)) { + Preconditions.checkNotNull(stream, "Unable to load resource " + resource); + return new NetconfMessage(XmlUtil.readXmlToDocument(stream)); + } catch (SAXException | IOException e) { + throw new RuntimeException("Unable to parse message from resources " + resource, e); + } + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java index b37c1457c3..27f930990d 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java @@ -8,7 +8,6 @@ package org.opendaylight.controller.netconf.persist.impl; -import com.google.common.base.Optional; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.config.persist.api.PropertiesProvider; @@ -17,6 +16,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.Collections; +import java.util.List; public class NoOpStorageAdapter implements StorageAdapter, Persister { private static final Logger logger = LoggerFactory.getLogger(NoOpStorageAdapter.class); @@ -33,9 +34,9 @@ public class NoOpStorageAdapter implements StorageAdapter, Persister { } @Override - public Optional loadLastConfig() throws IOException { + public List loadLastConfigs() throws IOException { logger.debug("loadLastConfig called"); - return Optional.absent(); + return Collections.emptyList(); } @Override diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregator.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregator.java index e109ebec88..7e9dce67bd 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregator.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregator.java @@ -9,7 +9,6 @@ package org.opendaylight.controller.netconf.persist.impl; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.config.persist.api.StorageAdapter; @@ -20,6 +19,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.ListIterator; @@ -48,7 +48,7 @@ import java.util.ListIterator; * During server startup {@link ConfigPersisterNotificationHandler} requests last snapshot from underlying storages. * Each storage can respond by giving snapshot or absent response. - * The {@link #loadLastConfig()} will search for first non-absent response from storages ordered backwards as user + * The {@link #loadLastConfigs()} will search for first non-absent response from storages ordered backwards as user * specified (first '3', then '2'). * * When a commit notification is received, '2' will be omitted because readonly flag is set to true, so @@ -154,19 +154,29 @@ public final class PersisterAggregator implements Persister { } } + /** + * @return last non-empty result from input persisters + */ @Override - public Optional loadLastConfig() throws IOException { + public List loadLastConfigs() { // iterate in reverse order ListIterator li = persisterWithConfigurations.listIterator(persisterWithConfigurations.size()); while(li.hasPrevious()) { PersisterWithConfiguration persisterWithConfiguration = li.previous(); - Optional configSnapshotHolderOptional = persisterWithConfiguration.storage.loadLastConfig(); - if (configSnapshotHolderOptional.isPresent()) { - return configSnapshotHolderOptional; + List configs = null; + try { + configs = persisterWithConfiguration.storage.loadLastConfigs(); + } catch (IOException e) { + throw new RuntimeException("Error while calling loadLastConfig on " + persisterWithConfiguration, e); + } + if (configs.isEmpty() == false) { + logger.debug("Found non empty configs using {}:{}", persisterWithConfiguration, configs); + return configs; } } // no storage had an answer - return Optional.absent(); + logger.debug("No non-empty list of configuration snapshots found"); + return Collections.emptyList(); } @VisibleForTesting diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java index b17309123c..811ba38c10 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java @@ -8,24 +8,79 @@ package org.opendaylight.controller.netconf.persist.impl; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; +import org.opendaylight.controller.config.api.ConflictingVersionException; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.client.NetconfClient; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; +import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import java.util.Set; public final class Util { + private static final Logger logger = LoggerFactory.getLogger(Util.class); + + + public static boolean isSubset(NetconfClient netconfClient, Set expectedCaps) { + return isSubset(netconfClient.getCapabilities(), expectedCaps); + + } + + private static boolean isSubset(Set currentCapabilities, Set expectedCaps) { + for (String exCap : expectedCaps) { + if (currentCapabilities.contains(exCap) == false) + return false; + } + return true; + } + + + // TODO: check if closing in correct order + public static void closeClientAndDispatcher(NetconfClient client) { + NetconfClientDispatcher dispatcher = client.getNetconfClientDispatcher(); + Exception fromClient = null; + try { + client.close(); + } catch (Exception e) { + fromClient = e; + } finally { + try { + dispatcher.close(); + } catch (Exception e) { + if (fromClient != null) { + e.addSuppressed(fromClient); + } + throw new RuntimeException("Error closing temporary client ", e); + } + } + } - public static ScheduledExecutorService getExecutorServiceWithThreadName(final String threadNamePrefix, - int threadCount) { - return Executors.newScheduledThreadPool(threadCount, new ThreadFactory() { - private int i = 1; + public static void checkIsOk(XmlElement element, NetconfMessage responseMessage) throws ConflictingVersionException { + if (element.getName().equals(XmlNetconfConstants.OK)) { + return; + } - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r); - thread.setName(threadNamePrefix + ":" + i++); - return thread; + if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) { + logger.warn("Can not load last configuration, operation failed"); + // is it ConflictingVersionException ? + XPathExpression xPathExpression = XMLNetconfUtil.compileXPath("/netconf:rpc-reply/netconf:rpc-error/netconf:error-info/netconf:error"); + String error = (String) XmlUtil.evaluateXPath(xPathExpression, element.getDomElement(), XPathConstants.STRING); + if (error!=null && error.contains(ConflictingVersionException.class.getCanonicalName())) { + throw new ConflictingVersionException(error); } - }); + throw new IllegalStateException("Can not load last configuration, operation failed: " + + XmlUtil.toString(responseMessage.getDocument())); + } + + logger.warn("Can not load last configuration. Operation failed."); + throw new IllegalStateException("Can not load last configuration. Operation failed: " + + XmlUtil.toString(responseMessage.getDocument())); } } 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 e7916c2d5f..656091115c 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,8 +8,13 @@ package org.opendaylight.controller.netconf.persist.impl.osgi; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import org.opendaylight.controller.netconf.client.NetconfClient; import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler; +import org.opendaylight.controller.netconf.persist.impl.ConfigPusher; import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator; +import org.opendaylight.controller.netconf.persist.impl.Util; import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; @@ -28,14 +33,19 @@ public class ConfigPersisterActivator implements BundleActivator { private final static MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer(); private static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex"; - private ConfigPersisterNotificationHandler configPersisterNotificationHandler; + public static final String NETCONF_CONFIG_PERSISTER = "netconf.config.persister"; - private Thread initializationThread; + public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX = "storageAdapterClass"; - public static final String NETCONF_CONFIG_PERSISTER = "netconf.config.persister"; - public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX = "storageAdapterClass"; public static final String DEFAULT_IGNORED_REGEX = "^urn:ietf:params:xml:ns:netconf:base:1.0"; + + private volatile ConfigPersisterNotificationHandler jmxNotificationHandler; + private volatile NetconfClient netconfClient; + private Thread initializationThread; + private EventLoopGroup nettyThreadgroup; + private PersisterAggregator persisterAggregator; + @Override public void start(final BundleContext context) throws Exception { logger.debug("ConfigPersister starting"); @@ -49,20 +59,24 @@ public class ConfigPersisterActivator implements BundleActivator { } else { regex = DEFAULT_IGNORED_REGEX; } - Pattern ignoredMissingCapabilityRegex = Pattern.compile(regex); - PersisterAggregator persister = PersisterAggregator.createFromProperties(propertiesProvider); + final Pattern ignoredMissingCapabilityRegex = Pattern.compile(regex); + nettyThreadgroup = new NioEventLoopGroup(); + + persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider); + final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context, "Netconf is not configured, persister is not operational", true); + final ConfigPusher configPusher = new ConfigPusher(address, nettyThreadgroup); - InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context, - "Netconf is not configured, persister is not operational",true); - configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(persister, address, - platformMBeanServer, ignoredMissingCapabilityRegex); // offload initialization to another thread in order to stop blocking activator Runnable initializationRunnable = new Runnable() { @Override public void run() { try { - configPersisterNotificationHandler.init(); + netconfClient = configPusher.init(persisterAggregator.loadLastConfigs()); + jmxNotificationHandler = new ConfigPersisterNotificationHandler( + platformMBeanServer, netconfClient, persisterAggregator, + ignoredMissingCapabilityRegex); + jmxNotificationHandler.init(); } catch (InterruptedException e) { logger.info("Interrupted while waiting for netconf connection"); } @@ -75,6 +89,18 @@ public class ConfigPersisterActivator implements BundleActivator { @Override public void stop(BundleContext context) throws Exception { initializationThread.interrupt(); - configPersisterNotificationHandler.close(); + if (jmxNotificationHandler != null) { + jmxNotificationHandler.close(); + } + if (netconfClient != null) { + netconfClient = jmxNotificationHandler.getNetconfClient(); + try { + Util.closeClientAndDispatcher(netconfClient); + } catch (Exception e) { + logger.warn("Unable to close connection to netconf {}", netconfClient, e); + } + } + nettyThreadgroup.shutdownGracefully(); + persisterAggregator.close(); } } diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandlerTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandlerTest.java index 6c45c9c011..a124d85b91 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandlerTest.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandlerTest.java @@ -24,7 +24,7 @@ public class ConfigPersisterNotificationHandlerTest { public void testConflictingVersionDetection() throws Exception { Document document = XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/conflictingVersionResponse.xml")); try{ - ConfigPersisterNotificationHandler.checkIsOk(XmlElement.fromDomDocument(document).getOnlyChildElement(), new NetconfMessage(document)); + Util.checkIsOk(XmlElement.fromDomDocument(document).getOnlyChildElement(), new NetconfMessage(document)); fail(); }catch(ConflictingVersionException e){ assertThat(e.getMessage(), containsString("Optimistic lock failed. Expected parent version 21, was 18")); diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/DummyAdapter.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/DummyAdapter.java index 7a11b9cd6f..e824b58832 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/DummyAdapter.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/DummyAdapter.java @@ -7,13 +7,14 @@ */ package org.opendaylight.controller.netconf.persist.impl; -import com.google.common.base.Optional; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.config.persist.api.PropertiesProvider; import org.opendaylight.controller.config.persist.api.StorageAdapter; import java.io.IOException; +import java.util.Collections; +import java.util.List; public class DummyAdapter implements StorageAdapter, Persister { @@ -27,9 +28,9 @@ public class DummyAdapter implements StorageAdapter, Persister { static int load = 0; @Override - public Optional loadLastConfig() throws IOException { + public List loadLastConfigs() throws IOException { load++; - return Optional.absent(); + return Collections.emptyList(); } static int props = 0; diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregatorTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregatorTest.java index d9fa7ba4d8..227018bf5b 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregatorTest.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregatorTest.java @@ -8,7 +8,6 @@ package org.opendaylight.controller.netconf.persist.impl; -import com.google.common.base.Optional; import org.junit.Test; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; @@ -18,14 +17,14 @@ import org.opendaylight.controller.netconf.persist.impl.osgi.PropertiesProviderB import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Properties; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import static org.junit.matchers.JUnitMatchers.containsString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -79,9 +78,9 @@ public class PersisterAggregatorTest { assertFalse(persister.isReadOnly()); persisterAggregator.persistConfig(null); - persisterAggregator.loadLastConfig(); + persisterAggregator.loadLastConfigs(); persisterAggregator.persistConfig(null); - persisterAggregator.loadLastConfig(); + persisterAggregator.loadLastConfigs(); assertEquals(2, DummyAdapter.persist); assertEquals(2, DummyAdapter.load); @@ -110,29 +109,41 @@ public class PersisterAggregatorTest { } } + private ConfigSnapshotHolder mockHolder(String name){ + ConfigSnapshotHolder result = mock(ConfigSnapshotHolder.class); + doReturn("mock:" + name).when(result).toString(); + return result; + } + + private Persister mockPersister(String name){ + Persister result = mock(Persister.class); + doReturn("mock:" + name).when(result).toString(); + return result; + } + @Test public void loadLastConfig() throws Exception { List persisterWithConfigurations = new ArrayList<>(); PersisterWithConfiguration first = new PersisterWithConfiguration(mock(Persister.class), false); - ConfigSnapshotHolder ignored = mock(ConfigSnapshotHolder.class); - doReturn(Optional.of(ignored)).when(first.getStorage()).loadLastConfig(); // should be ignored + ConfigSnapshotHolder ignored = mockHolder("ignored"); + doReturn(Arrays.asList(ignored)).when(first.getStorage()).loadLastConfigs(); // should be ignored - ConfigSnapshotHolder used = mock(ConfigSnapshotHolder.class); - PersisterWithConfiguration second = new PersisterWithConfiguration(mock(Persister.class), false); - doReturn(Optional.of(used)).when(second.getStorage()).loadLastConfig(); // should be used - PersisterWithConfiguration third = new PersisterWithConfiguration(mock(Persister.class), false); - doReturn(Optional.absent()).when(third.getStorage()).loadLastConfig(); + ConfigSnapshotHolder used = mockHolder("used"); + PersisterWithConfiguration second = new PersisterWithConfiguration(mockPersister("p1"), false); + doReturn(Arrays.asList(used)).when(second.getStorage()).loadLastConfigs(); // should be used + + PersisterWithConfiguration third = new PersisterWithConfiguration(mockPersister("p2"), false); + doReturn(Arrays.asList()).when(third.getStorage()).loadLastConfigs(); persisterWithConfigurations.add(first); persisterWithConfigurations.add(second); persisterWithConfigurations.add(third); PersisterAggregator persisterAggregator = new PersisterAggregator(persisterWithConfigurations); - Optional configSnapshotHolderOptional = persisterAggregator.loadLastConfig(); - assertTrue(configSnapshotHolderOptional.isPresent()); - assertEquals(used, configSnapshotHolderOptional.get()); + List configSnapshotHolderOptional = persisterAggregator.loadLastConfigs(); + assertEquals(1, configSnapshotHolderOptional.size()); + assertEquals(used, configSnapshotHolderOptional.get(0)); } - } diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java index 1e14bfdba2..eb7235f044 100644 --- a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java @@ -58,7 +58,7 @@ public abstract class NetconfSession extends AbstractProtocolSession urn:ietf:params:netconf:base:1.0 + urn:ietf:params:netconf:base:1.1 urn:ietf:params:netconf:capability:exi:1.0 \ No newline at end of file diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java index ccc7c85eb3..c61dab7f64 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java @@ -8,23 +8,41 @@ package org.opendaylight.controller.netconf.it; -import ch.ethz.ssh2.Connection; -import ch.ethz.ssh2.Session; -import com.google.common.base.Optional; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import static java.util.Collections.emptyList; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.HashedWheelTimer; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.management.ObjectName; +import javax.xml.parsers.ParserConfigurationException; + import junit.framework.Assert; + import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; -import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.config.spi.ModuleFactory; import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; import org.opendaylight.controller.config.yang.store.api.YangStoreException; @@ -49,8 +67,6 @@ import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFact import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl; import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; -import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler; -import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator; import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; import org.opendaylight.controller.netconf.util.test.XmlFileLoader; import org.opendaylight.controller.netconf.util.xml.ExiParameters; @@ -64,28 +80,11 @@ import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.SAXException; -import javax.management.ObjectName; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.InputStream; -import java.lang.management.ManagementFactory; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; +import ch.ethz.ssh2.Connection; +import ch.ethz.ssh2.Session; -import static java.util.Collections.emptyList; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; public class NetconfITTest extends AbstractConfigTest { @@ -220,14 +219,18 @@ public class NetconfITTest extends AbstractConfigTest { } } + + //TODO: test persister actually + @Ignore @Test(timeout = 10000) public void testPersister() throws Exception { - Persister persister = mock(Persister.class); - doReturn("mockPersister").when(persister).toString(); - doReturn(Optional.absent()).when(persister).loadLastConfig(); - ConfigPersisterNotificationHandler h = - new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer(), Pattern.compile(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX)); - h.init(); +// Persister persister = mock(Persister.class); +// doReturn("mockPersister").when(persister).toString(); +// doReturn(Collections.emptyList()).when(persister).loadLastConfigs(); +// ConfigPersisterNotificationHandler h = +// new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer(), +// Pattern.compile(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX)); +// h.init(); } @Ignore diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml index 4e34591284..46f6f76412 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml @@ -1,5 +1,6 @@ urn:ietf:params:netconf:base:1.0 + urn:ietf:params:netconf:base:1.1 diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml index caa0094c4b..94b73f4b10 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml @@ -32,15 +32,15 @@ test1 - 2.00 + 2.58 - 1 + 10 - 1 + 20 @@ -48,9 +48,8 @@ 44 - 8 - 1 - 0 + YmluYXJ5 + configAttributeType 444 @@ -94,6 +93,15 @@ prefix:testing ref_dep + + + prefix:testing + ref_dep + + + prefix:testing + ref_dep_2 + @@ -105,7 +113,7 @@ prefix:testing - ref_dep + ref_dep_2 @@ -115,7 +123,7 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] @@ -125,7 +133,8 @@ ref_test1 - /config/modules/module[name='impl-netconf']/instance[name='test1'] + + /modules/module[type='impl-netconf'][name='test1'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml index c825536b7b..b48730d3f7 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml @@ -27,9 +27,7 @@ test1 44 - 8 - 7 - 9 + 8ad1 444 4444 @@ -89,17 +87,18 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] ref_dep_2 - /config/modules/module[name='impl-dep']/instance[name='dep2'] + /modules/module[type='impl-dep'][name='dep2'] ref_test1 - /config/modules/module[name='impl-netconf']/instance[name='test1'] + + /modules/module[type='impl-netconf'][name='test1'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml index 82c218dd73..df2a5e8452 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml @@ -32,9 +32,7 @@ test1 44 - 8 - 1 - 0 + 8545649856 configAttributeType 444 @@ -97,17 +95,18 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] ref_dep_2 - /config/modules/module[name='impl-dep']/instance[name='dep2'] + /modules/module[type='impl-dep'][name='dep2'] ref_test1 - /config/modules/module[name='impl-netconf']/instance[name='test1'] + + /modules/module[type='impl-netconf'][name='test1'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml index 7c19b9f7b3..c399c196d3 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml @@ -98,17 +98,18 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] ref_dep_2 - /config/modules/module[name='impl-dep']/instance[name='dep2'] + /modules/module[type='impl-dep'][name='dep2'] ref_test1 - /config/modules/module[name='impl-netconf']/instance[name='test1'] + + /modules/module[type='impl-netconf'][name='test1'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml index 84ae575559..47b82c6114 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml @@ -97,17 +97,18 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] ref_dep_2 - /config/modules/module[name='impl-dep']/instance[name='dep2'] + /modules/module[type='impl-dep'][name='dep2'] ref_test1 - /config/modules/module[name='impl-netconf']/instance[name='test1'] + + /modules/module[type='impl-netconf'][name='test1'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml index 2d9e9edb2c..02aca8d787 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml @@ -32,9 +32,7 @@ test1 44 - 8 - 1 - 0 + 8545649856 444 4444 @@ -96,17 +94,18 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] ref_dep_2 - /config/modules/module[name='impl-dep']/instance[name='dep2'] + /modules/module[type='impl-dep'][name='dep2'] ref_test1 - /config/modules/module[name='impl-netconf']/instance[name='test1'] + + /modules/module[type='impl-netconf'][name='test1'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml index 9407dd0c75..bc05d63931 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml @@ -27,7 +27,7 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml index 233ad23286..f20d9ff9fa 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml @@ -28,7 +28,7 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml index 4593887f49..ff0bd9feb5 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml @@ -29,7 +29,7 @@ ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml index 81c4137d4e..906367b7a6 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml @@ -29,7 +29,7 @@ ref_dep error - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml index 528d5b06fc..b05046db4b 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml @@ -26,7 +26,7 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml index a1e304b374..825be6d19f 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml @@ -32,9 +32,7 @@ test1 44 - 8 - 1 - 0 + 8545649856 error 444 @@ -97,17 +95,18 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] ref_dep_2 - /config/modules/module[name='impl-dep']/instance[name='dep2'] + /modules/module[type='impl-dep'][name='dep2'] ref_test1 - /config/modules/module[name='impl-netconf']/instance[name='test1'] + + /modules/module[type='impl-netconf'][name='test1'] diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml index 812882a2a9..9ef2bed7d7 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml @@ -32,9 +32,7 @@ test1 44 - 8 - 1 - 0 + 8545649856 444 4444 @@ -97,17 +95,18 @@ prefix:testing ref_dep - /config/modules/module[name='impl-dep']/instance[name='dep'] + /modules/module[type='impl-dep'][name='dep'] ref_dep_2 - /config/modules/module[name='impl-dep']/instance[name='dep2'] + /modules/module[type='impl-dep'][name='dep2'] ref_test1 - /config/modules/module[name='impl-netconf']/instance[name='test1'] + + /modules/module[type='impl-netconf'][name='test1'] diff --git a/opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java b/opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java index 8a7c3983d1..259ffde4b4 100644 --- a/opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java +++ b/opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java @@ -142,17 +142,17 @@ public class Flows implements IDaylightWeb { } Map nodes = new HashMap(); - Map port; + Map port; for (Switch node : switchManager.getNetworkDevices()) { - port = new HashMap(); // new port + port = new HashMap(); // new port Set nodeConnectorSet = node.getNodeConnectors(); if (nodeConnectorSet != null) { for (NodeConnector nodeConnector : nodeConnectorSet) { String nodeConnectorName = ((Name) switchManager.getNodeConnectorProp(nodeConnector, Name.NamePropName)).getValue(); - port.put((Short) nodeConnector.getID(), + port.put( nodeConnector.getID().toString(), nodeConnectorName + "(" + nodeConnector.getNodeConnectorIDString() + ")"); } } diff --git a/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/DaylightWebAdmin.java b/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/DaylightWebAdmin.java index 3c28152c25..4c8a6b8439 100644 --- a/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/DaylightWebAdmin.java +++ b/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/DaylightWebAdmin.java @@ -143,17 +143,22 @@ public class DaylightWebAdmin { return gson.toJson(result); } - @RequestMapping("/users") + @RequestMapping(value = "/users", method = RequestMethod.GET) @ResponseBody - public List getUsers() { + public List getUsers() { IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this); if (userManager == null) { return null; } - List userConfList = userManager.getLocalUserList(); + List result = new ArrayList(); + List configs = userManager.getLocalUserList(); + for (UserConfig config : configs) { + UserBean bean = new UserBean(config); + result.add(bean); + } - return userConfList; + return result; } /* diff --git a/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/UserBean.java b/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/UserBean.java new file mode 100644 index 0000000000..4d30ed34e9 --- /dev/null +++ b/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/UserBean.java @@ -0,0 +1,27 @@ +package org.opendaylight.controller.web; + +import java.util.List; + +import org.opendaylight.controller.usermanager.UserConfig; + +public class UserBean { + private String user; + private List roles; + + public UserBean(String user, List roles) { + this.user = user; + this.roles = roles; + } + + public UserBean(UserConfig config) { + this(config.getUser(), config.getRoles()); + } + + public String getUser() { + return user; + } + + public List getRoles() { + return roles; + } +} \ No newline at end of file