From: Ed Warnicke Date: Fri, 29 Nov 2013 11:17:38 +0000 (+0000) Subject: Merge changes Ia268965a,Iefa79f99 X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~304 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=bab3649670c33b3b12a49c59fc1c5cabcbcb924e;hp=d431c454a362865e1b504ecc87f199836403b0e8 Merge changes Ia268965a,Iefa79f99 * changes: Add running configuration data to get netconf operation response Add support for configuration attributes based on yang leaves with custom types (typedef) to netconf. --- diff --git a/.gitignore b/.gitignore index bb3e26faf5..18817e7c35 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ opendaylight/northbound/integrationtest/logs/* *.iws .idea xtend-gen +classes diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 794986a7bb..a652845770 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -1126,7 +1126,9 @@ - + + false + 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 new file mode 100644 index 0000000000..654326a898 --- /dev/null +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/ConfigSnapshotHolder.java @@ -0,0 +1,17 @@ +package org.opendaylight.controller.config.persist.api; + +import java.util.SortedSet; + +public interface ConfigSnapshotHolder { + + /** + * Get part of get-config document that contains just + */ + String getConfigSnapshot(); + + + /** + * 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/Persister.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java index f9c9301b88..1448e553e3 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 @@ -10,23 +10,18 @@ package org.opendaylight.controller.config.persist.api; import com.google.common.base.Optional; -import java.io.Closeable; import java.io.IOException; -import java.util.Set; /** * Base interface for persister implementation. */ -public interface Persister extends Closeable { +public interface Persister extends AutoCloseable { void persistConfig(ConfigSnapshotHolder configSnapshotHolder) throws IOException; Optional loadLastConfig() throws IOException; - public static interface ConfigSnapshotHolder { + @Override + void close(); - String getConfigSnapshot(); - - Set getCapabilities(); - } } diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/PropertiesProvider.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/PropertiesProvider.java new file mode 100644 index 0000000000..1d4139f885 --- /dev/null +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/PropertiesProvider.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.persist.api; + +public interface PropertiesProvider { + /** + * Get property value for given key. Implementation of this interface is allowed to prefix + * the key with a namespace. + */ + String getProperty(String key); + + /** + * @return prefix + key as used in getProperty method. + */ + String getFullKeyForReporting(String key); +} diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/StorageAdapter.java similarity index 61% rename from opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java rename to opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/StorageAdapter.java index 9daf4a1325..524e7b5633 100644 --- a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/StorageAdapter.java @@ -6,17 +6,15 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.config.persist.api.storage; - -import org.opendaylight.controller.config.persist.api.Persister; -import org.osgi.framework.BundleContext; +package org.opendaylight.controller.config.persist.api; /** * Plugins for {@link org.opendaylight.controller.config.persist.api.Persister} * must implement this interface. */ -public interface StorageAdapter extends Persister { +public interface StorageAdapter { + + Persister instantiate(PropertiesProvider propertiesProvider); - void setProperties(BundleContext bundleContext); } diff --git a/opendaylight/config/config-persister-directory-adapter/pom.xml b/opendaylight/config/config-persister-directory-adapter/pom.xml new file mode 100644 index 0000000000..b2da71eea2 --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + config-subsystem + org.opendaylight.controller + 0.2.3-SNAPSHOT + .. + + config-persister-directory-adapter + ${project.artifactId} + bundle + + + + + ${project.groupId} + config-persister-api + + + org.apache.commons + commons-lang3 + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + commons-io + commons-io + + + + + org.opendaylight.bgpcep + mockito-configuration + test + + + + + + + + + org.codehaus.groovy.maven + gmaven-plugin + + + generate-sources + + execute + + + + System.setProperty("osgiversion", "${project.version}".replace('-', '.')) + + + + + + + org.apache.felix + maven-bundle-plugin + + + ${project.groupId}.config-persister-impl;bundle-version=${osgiversion} + + org.opendaylight.controller.config.persister.storage.adapter + + + com.google.common.base, + com.google.common.io, + org.apache.commons.io, + org.opendaylight.controller.config.persist.api, + org.slf4j + + + + + + + + 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 new file mode 100644 index 0000000000..cf4ef98bdc --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryPersister.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.persist.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.Persister; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +public class DirectoryPersister implements Persister { + private static final Logger logger = LoggerFactory.getLogger(DirectoryPersister.class); + private static final Charset ENCODING = Charsets.UTF_8; + + static final String MODULES_START = "//MODULES START"; + static final String SERVICES_START = "//SERVICES START"; + static final String CAPABILITIES_START = "//CAPABILITIES START"; + + + private final File storage; + private final String header, middle, footer; + + public DirectoryPersister(File storage) { + checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage); + this.storage = storage; + header = readResource("header.txt"); + middle = readResource("middle.txt"); + footer = readResource("footer.txt"); + + } + + private static String readResource(String resource) { + try { + return IOUtils.toString(DirectoryPersister.class.getResourceAsStream("/" + resource)); + } catch (IOException e) { + throw new IllegalStateException("Cannot load " + resource, e); + } + } + + @Override + public void persistConfig(ConfigSnapshotHolder holder) throws IOException { + throw new UnsupportedOperationException("This adapter is read only. Please set readonly=true on " + getClass()); + } + + @Override + public Optional loadLastConfig() throws IOException { + File[] filesArray = storage.listFiles(); + if (filesArray.length == 0) { + return Optional.absent(); + } + List sortedFiles = new ArrayList<>(Arrays.asList(filesArray)); + Collections.sort(sortedFiles); + // combine all found files + + SortedSet combinedCapabilities = new TreeSet<>(); + StringBuilder modulesBuilder = new StringBuilder(), servicesBuilder = new StringBuilder(); + for (File file : sortedFiles) { + logger.trace("Adding file '{}' to combined result", file); + + final MyLineProcessor lineProcessor = new MyLineProcessor(); + Files.readLines(file, ENCODING, lineProcessor); + + modulesBuilder.append(lineProcessor.getModules()); + servicesBuilder.append(lineProcessor.getServices()); + combinedCapabilities.addAll(lineProcessor.getCapabilities()); + } + String combinedSnapshot = header + modulesBuilder.toString() + middle + servicesBuilder.toString() + footer; + ConfigSnapshotHolder result = new ConfigSnapshotHolderImpl(combinedSnapshot, combinedCapabilities); + return Optional.of(result); + } + + + @Override + public void close() { + + } + + @Override + public String toString() { + return "FileStorageAdapter [storage=" + storage + "]"; + } +} + +class MyLineProcessor implements com.google.common.io.LineProcessor { + + private boolean inModules, inServices, inCapabilities; + private final StringBuffer modulesBuffer = new StringBuffer(), servicesBuilder = new StringBuffer(); + private final SortedSet caps = new TreeSet<>(); + + @Override + public String getResult() { + return null; + } + + @Override + public boolean processLine(String line) throws IOException { + + String lineWithNewLine = line + System.lineSeparator(); + if (line.equals(DirectoryPersister.MODULES_START)) { + checkState(inModules == false && inServices == false && inCapabilities == false); + inModules = true; + } else if (line.equals(DirectoryPersister.SERVICES_START)) { + checkState(inModules == true && inServices == false && inCapabilities == false); + inModules = false; + inServices = true; + } else if (line.equals(DirectoryPersister.CAPABILITIES_START)) { + checkState(inModules == false && inServices == true && inCapabilities == false); + inServices = false; + inCapabilities = true; + } else if (inModules) { + modulesBuffer.append(lineWithNewLine); + } else if (inServices) { + servicesBuilder.append(lineWithNewLine); + } else { + caps.add(line); + } + return true; + } + + String getModules() { + checkState(inCapabilities); + return modulesBuffer.toString(); + } + + String getServices() { + checkState(inCapabilities); + return servicesBuilder.toString(); + } + + SortedSet getCapabilities() { + checkState(inCapabilities); + 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; + } + + @Override + public SortedSet getCapabilities() { + return caps; + } +} diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapter.java b/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapter.java new file mode 100644 index 0000000000..69c8fba9da --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapter.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.config.persist.storage.directory; + +import com.google.common.base.Preconditions; +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 java.io.File; + +/** + * StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and + * required capabilities will be merged together. Writing to this persister is not supported. + */ +public class DirectoryStorageAdapter implements StorageAdapter { + private static final Logger logger = LoggerFactory.getLogger(DirectoryStorageAdapter.class); + + public static final String DIRECTORY_STORAGE_PROP = "directoryStorage"; + + + @Override + public Persister instantiate(PropertiesProvider propertiesProvider) { + String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP); + Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP)); + File storage = new File(fileStorageProperty); + logger.debug("Using {}", storage); + return new DirectoryPersister(storage); + } + +} diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/resources/footer.txt b/opendaylight/config/config-persister-directory-adapter/src/main/resources/footer.txt new file mode 100644 index 0000000000..d4dcc62822 --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/main/resources/footer.txt @@ -0,0 +1,2 @@ + + diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/resources/header.txt b/opendaylight/config/config-persister-directory-adapter/src/main/resources/header.txt new file mode 100644 index 0000000000..90ed41c3da --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/main/resources/header.txt @@ -0,0 +1,2 @@ + + diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/resources/middle.txt b/opendaylight/config/config-persister-directory-adapter/src/main/resources/middle.txt new file mode 100644 index 0000000000..f728cfdd95 --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/main/resources/middle.txt @@ -0,0 +1,2 @@ + + 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 new file mode 100644 index 0000000000..53ab4c210e --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapterTest.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.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.SortedSet; +import java.util.TreeSet; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +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()); + + try { + tested.persistConfig(new ConfigSnapshotHolder() { + @Override + public String getConfigSnapshot() { + throw new RuntimeException(); + } + + @Override + public SortedSet getCapabilities() { + throw new RuntimeException(); + } + }); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + private File getFolder(String folderName) { + File result = new File(("src/test/resources/" + + folderName).replace("/", File.separator)); + assertTrue(result + " is not a directory", result.isDirectory()); + return result; + } + + @Test + public void testOneFile() throws Exception { + File folder = getFolder("oneFile"); + tested = new DirectoryPersister((folder)); + assertExpected(); + } + + 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(); + } + +} diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/expectedCapabilities.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/expectedCapabilities.txt new file mode 100644 index 0000000000..84c85b740c --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/resources/expectedCapabilities.txt @@ -0,0 +1,20 @@ +urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27 +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/expectedSnapshot.xml b/opendaylight/config/config-persister-directory-adapter/src/test/resources/expectedSnapshot.xml new file mode 100644 index 0000000000..a6a57d704a --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/resources/expectedSnapshot.xml @@ -0,0 +1,103 @@ + + + + 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 + + + + + + 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'] + + + + diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFile/controller.config.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFile/controller.config.txt new file mode 100644 index 0000000000..99b4cb9891 --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFile/controller.config.txt @@ -0,0 +1,120 @@ +//MODULES START + + 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 + + +//SERVICES START + + 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'] + + +//CAPABILITIES START +urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27 +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/twoFiles/controller.config1.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config1.txt new file mode 100644 index 0000000000..6ae48fb84b --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config1.txt @@ -0,0 +1,82 @@ +//MODULES START + + 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 + +//SERVICES START + + 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'] + + +//CAPABILITIES START +urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27 diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config2.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config2.txt new file mode 100644 index 0000000000..ad9138f5d2 --- /dev/null +++ b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config2.txt @@ -0,0 +1,41 @@ +//MODULES START + + prefix:binding-data-broker + binding-data-broker + + dom:dom-broker-osgi-registry + ref_dom-broker + + + binding:binding-dom-mapping-service + ref_runtime-mapping-singleton + + +//SERVICES START + + binding:binding-data-broker + + ref_binding-data-broker + /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker'] + + +//CAPABILITIES START +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-file-adapter/pom.xml b/opendaylight/config/config-persister-file-adapter/pom.xml index b243eadcc6..d34dc37707 100644 --- a/opendaylight/config/config-persister-file-adapter/pom.xml +++ b/opendaylight/config/config-persister-file-adapter/pom.xml @@ -80,7 +80,6 @@ javax.xml.transform.stream, org.apache.commons.lang3, org.opendaylight.controller.config.persist.api, - org.opendaylight.controller.config.persist.api.storage, org.slf4j, org.w3c.dom, org.xml.sax, diff --git a/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java b/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java index 775fb1f881..66d0414d9a 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 @@ -12,11 +12,12 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import com.google.common.collect.Sets; import com.google.common.io.Files; import org.apache.commons.lang3.StringUtils; -import org.opendaylight.controller.config.persist.api.storage.StorageAdapter; -import org.osgi.framework.BundleContext; +import org.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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; @@ -26,20 +27,22 @@ import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; /** * StorageAdapter that stores configuration in a plan file. */ -public class FileStorageAdapter implements StorageAdapter { +public class FileStorageAdapter implements StorageAdapter, Persister { private static final Logger logger = LoggerFactory.getLogger(FileStorageAdapter.class); - // TODO prefix properties private static final Charset ENCODING = Charsets.UTF_8; public static final String FILE_STORAGE_PROP = "fileStorage"; public static final String NUMBER_OF_BACKUPS = "numberOfBackups"; + private static final String SEPARATOR_E_PURE = "//END OF CONFIG"; private static final String SEPARATOR_E = newLine(SEPARATOR_E_PURE); @@ -55,8 +58,8 @@ public class FileStorageAdapter implements StorageAdapter { private File storage; @Override - public void setProperties(BundleContext bundleContext) { - File storage = extractStorageFileFromProperties(bundleContext); + public Persister instantiate(PropertiesProvider propertiesProvider) { + File storage = extractStorageFileFromProperties(propertiesProvider); logger.debug("Using file {}", storage.getAbsolutePath()); // Create file if it does not exist File parentFile = storage.getAbsoluteFile().getParentFile(); @@ -79,7 +82,7 @@ public class FileStorageAdapter implements StorageAdapter { + " property should be either set to positive value, or ommited. Can not be set to 0."); } setFileStorage(storage); - + return this; } @VisibleForTesting @@ -92,12 +95,11 @@ public class FileStorageAdapter implements StorageAdapter { numberOfStoredBackups = numberOfBackups; } - private static File extractStorageFileFromProperties(BundleContext bundleContext) { - String fileStorageProperty = bundleContext.getProperty(FILE_STORAGE_PROP); - Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + FILE_STORAGE_PROP - + " in received context :" + bundleContext); + private static File extractStorageFileFromProperties(PropertiesProvider propertiesProvider) { + String fileStorageProperty = propertiesProvider.getProperty(FILE_STORAGE_PROP); + Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(FILE_STORAGE_PROP)); File result = new File(fileStorageProperty); - String numberOfBAckupsAsString = bundleContext.getProperty(NUMBER_OF_BACKUPS); + String numberOfBAckupsAsString = propertiesProvider.getProperty(NUMBER_OF_BACKUPS); if (numberOfBAckupsAsString != null) { numberOfStoredBackups = Integer.valueOf(numberOfBAckupsAsString); } else { @@ -189,7 +191,7 @@ public class FileStorageAdapter implements StorageAdapter { private boolean inLastConfig, inLastSnapshot; private final StringBuffer snapshotBuffer = new StringBuffer(); - private final Set caps = Sets.newHashSet(); + private final SortedSet caps = new TreeSet<>(); @Override public String getResult() { @@ -233,14 +235,14 @@ public class FileStorageAdapter implements StorageAdapter { return Optional.of(xmlContent); } - Set getCapabilities() throws IOException, SAXException, ParserConfigurationException { + SortedSet getCapabilities() throws IOException, SAXException, ParserConfigurationException { return caps; } } @Override - public void close() throws IOException { + public void close() { } @@ -252,9 +254,9 @@ public class FileStorageAdapter implements StorageAdapter { private class PersistedConfigImpl implements ConfigSnapshotHolder { private final String snapshot; - private final Set caps; + private final SortedSet caps; - public PersistedConfigImpl(Optional configSnapshot, Set capabilities) { + public PersistedConfigImpl(Optional configSnapshot, SortedSet capabilities) { this.snapshot = configSnapshot.get(); this.caps = capabilities; } @@ -265,7 +267,7 @@ public class FileStorageAdapter implements StorageAdapter { } @Override - public Set getCapabilities() { + 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 c7d37dcbd8..ed50184aa7 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 @@ -12,17 +12,16 @@ 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 com.google.common.collect.Sets; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import org.opendaylight.controller.config.persist.api.Persister; +import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import java.io.File; import java.nio.file.Files; import java.util.Collection; -import java.util.Collections; -import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -49,14 +48,14 @@ public class FileStorageAdapterTest { FileStorageAdapter storage = new FileStorageAdapter(); storage.setFileStorage(file); storage.setNumberOfBackups(Integer.MAX_VALUE); - final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() { + final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() { @Override public String getConfigSnapshot() { return createConfig(); } @Override - public Set getCapabilities() { + public SortedSet getCapabilities() { return createCaps(); } }; @@ -76,15 +75,15 @@ public class FileStorageAdapterTest { }); assertEquals(14, readLines.size()); - Optional lastConf = storage.loadLastConfig(); + Optional lastConf = storage.loadLastConfig(); assertTrue(lastConf.isPresent()); assertEquals("2", lastConf.get().getConfigSnapshot().replaceAll("\\s", "")); assertEquals(createCaps(), lastConf.get().getCapabilities()); } - private Set createCaps() { - Set caps = Sets.newHashSet(); + private SortedSet createCaps() { + SortedSet caps = new TreeSet<>(); caps.add("cap1"); caps.add("cap2"); @@ -97,14 +96,14 @@ public class FileStorageAdapterTest { FileStorageAdapter storage = new FileStorageAdapter(); storage.setFileStorage(file); storage.setNumberOfBackups(1); - final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() { + final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() { @Override public String getConfigSnapshot() { return createConfig(); } @Override - public Set getCapabilities() { + public SortedSet getCapabilities() { return createCaps(); } }; @@ -124,7 +123,7 @@ public class FileStorageAdapterTest { }); assertEquals(7, readLines.size()); - Optional lastConf = storage.loadLastConfig(); + Optional lastConf = storage.loadLastConfig(); assertTrue(lastConf.isPresent()); assertEquals("2", lastConf.get().getConfigSnapshot().replaceAll("\\s", "")); @@ -135,14 +134,14 @@ public class FileStorageAdapterTest { FileStorageAdapter storage = new FileStorageAdapter(); storage.setFileStorage(file); storage.setNumberOfBackups(2); - final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() { + final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() { @Override public String getConfigSnapshot() { return createConfig(); } @Override - public Set getCapabilities() { + public SortedSet getCapabilities() { return createCaps(); } }; @@ -164,7 +163,7 @@ public class FileStorageAdapterTest { assertEquals(14, readLines.size()); - Optional lastConf = storage.loadLastConfig(); + Optional lastConf = storage.loadLastConfig(); assertTrue(lastConf.isPresent()); assertEquals("3", lastConf.get().getConfigSnapshot().replaceAll("\\s", "")); @@ -179,7 +178,7 @@ public class FileStorageAdapterTest { FileStorageAdapter storage = new FileStorageAdapter(); storage.setFileStorage(file); - Optional elementOptional = storage.loadLastConfig(); + Optional elementOptional = storage.loadLastConfig(); assertThat(elementOptional.isPresent(), is(false)); } @@ -192,15 +191,15 @@ public class FileStorageAdapterTest { @Test(expected = NullPointerException.class) public void testNoProperties2() throws Exception { FileStorageAdapter storage = new FileStorageAdapter(); - storage.persistConfig(new Persister.ConfigSnapshotHolder() { + storage.persistConfig(new ConfigSnapshotHolder() { @Override public String getConfigSnapshot() { return Mockito.mock(String.class); } @Override - public Set getCapabilities() { - return Collections. emptySet(); + public SortedSet getCapabilities() { + return new TreeSet<>(); } } ); } diff --git a/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImpl.java b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImpl.java index 205f123b1d..02fba141b3 100644 --- a/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImpl.java +++ b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImpl.java @@ -101,13 +101,15 @@ public class ContextSetterImpl implements ContextSetter, Closeable { private void addNewAppenders(Map> appendersMap, LoggerTO logger, ch.qos.logback.classic.Logger logbackLogger, Optional>> appendersBefore) { - for (String appenderName : logger.getAppenders()) { - if (appendersMap.containsKey(appenderName)) { - logbackLogger.addAppender(appendersMap.get(appenderName)); - classLogger.trace("Logger {}: Adding new appender: {}", logger.getLoggerName(), appenderName); - } else { - throw new IllegalStateException("No appender " + appenderName - + " found. This error should have been discovered by validation"); + if (logger.getAppenders() != null) { + for (String appenderName : logger.getAppenders()) { + if (appendersMap.containsKey(appenderName)) { + logbackLogger.addAppender(appendersMap.get(appenderName)); + classLogger.trace("Logger {}: Adding new appender: {}", logger.getLoggerName(), appenderName); + } else { + throw new IllegalStateException("No appender " + appenderName + + " found. This error should have been discovered by validation"); + } } } } diff --git a/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModule.java b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModule.java index 9cbcd47b20..d75bf6367a 100644 --- a/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModule.java +++ b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModule.java @@ -54,11 +54,12 @@ public final class LogbackModule extends org.opendaylight.controller.config.yang "LoggerName needs to be set", loggersJmxAttribute); JmxAttributeValidationException.checkCondition(!loggerToValidate.getLevel().isEmpty(), "Level needs to be set", loggersJmxAttribute); - - for (String appenderName : loggerToValidate.getAppenders()) { - JmxAttributeValidationException.checkCondition(appenderNames.contains(appenderName), "Appender " - + appenderName + " referenced by logger " + loggerToValidate.getLoggerName() - + " not present in configuration, present appenders: " + appenderNames, loggersJmxAttribute); + if (loggerToValidate.getAppenders() != null) { + for (String appenderName : loggerToValidate.getAppenders()) { + JmxAttributeValidationException.checkCondition(appenderNames.contains(appenderName), "Appender " + + appenderName + " referenced by logger " + loggerToValidate.getLoggerName() + + " not present in configuration, present appenders: " + appenderNames, loggersJmxAttribute); + } } } diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index 22be6f162b..c1eef701da 100644 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -35,6 +35,7 @@ netty-threadgroup-config netty-event-executor-config netty-timer-config + config-persister-directory-adapter diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 5f2651659d..d1b1e73636 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -216,7 +216,11 @@ config-persister-file-adapter ${config.version} - + + org.opendaylight.controller + config-persister-directory-adapter + ${config.version} + diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini index 00c46d9529..b5bdb9e903 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini @@ -17,13 +17,19 @@ osgi.bundles=\ netconf.tcp.address=0.0.0.0 netconf.tcp.port=8383 - netconf.ssh.address=0.0.0.0 netconf.ssh.port=1830 -netconf.config.persister.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter -fileStorage=configuration/controller.config -numberOfBackups=1 +netconf.config.persister.active=1,2 +# read startup configuration +netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.DirectoryStorageAdapter +netconf.config.persister.1.properties.directoryStorage=configuration/initial/ +netconf.config.persister.1.readonly=true + +netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter +netconf.config.persister.2.properties.fileStorage=configuration/current/controller.currentconfig.txt + + yangstore.blacklist=.*controller.model.* # Set Default start level for framework diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/controller-config.1.txt b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/controller-config.1.txt new file mode 100644 index 0000000000..689b45ad3b --- /dev/null +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/controller-config.1.txt @@ -0,0 +1,5 @@ +//MODULES START +//SERVICES START + +//CAPABILITIES START +urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16 diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/controller.config b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/controller-config.2.txt similarity index 94% rename from opendaylight/distribution/opendaylight/src/main/resources/configuration/controller.config rename to opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/controller-config.2.txt index 947c15b35c..acae8866ce 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/controller.config +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/controller-config.2.txt @@ -1,6 +1,4 @@ -//START OF CONFIG-LAST - - +//MODULES START prefix:schema-service-singleton yang-schema-service @@ -49,8 +47,7 @@ ref_runtime-mapping-singleton - - +//SERVICES START dom:schema-service @@ -100,10 +97,7 @@ /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker'] - - - -//END OF SNAPSHOT +//CAPABILITIES START urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27 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 @@ -119,9 +113,7 @@ 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 -//END OF CONFIG diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml b/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml index 3ad0c61d4f..2815422274 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml @@ -36,7 +36,6 @@ - diff --git a/opendaylight/md-sal/model/pom.xml b/opendaylight/md-sal/model/pom.xml index 34ad23844f..2f3b7a036c 100644 --- a/opendaylight/md-sal/model/pom.xml +++ b/opendaylight/md-sal/model/pom.xml @@ -40,6 +40,7 @@ ${project.groupId}.${project.artifactId} + *,org.opendaylight.yangtools.yang.binding.annotations diff --git a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java index 0eace4daac..18c93ec6d9 100644 --- a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java +++ b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java @@ -41,7 +41,7 @@ public class TestHelper { mavenBundle("org.opendaylight.bgpcep", "framework").versionAsInProject(), // mavenBundle("org.opendaylight.bgpcep", "util").versionAsInProject(), // mavenBundle("commons-codec", "commons-codec").versionAsInProject(), - + mavenBundle(CONTROLLER, "config-api").versionAsInProject(), // // mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), // // mavenBundle("commons-io", "commons-io").versionAsInProject(), // @@ -54,30 +54,30 @@ public class TestHelper { mavenBundle(CONTROLLER, "logback-config").versionAsInProject(), // mavenBundle(CONTROLLER, "config-persister-api").versionAsInProject(), // mavenBundle(CONTROLLER, "netconf-api").versionAsInProject(), // - + mavenBundle(CONTROLLER, "netconf-client").versionAsInProject(), // mavenBundle(CONTROLLER, "netconf-util").versionAsInProject(), // mavenBundle(CONTROLLER + ".thirdparty", "ganymed", "1.0-SNAPSHOT"), // mavenBundle(CONTROLLER, "netconf-mapping-api").versionAsInProject(), // mavenBundle(CONTROLLER, "config-persister-impl").versionAsInProject(), // - + mavenBundle("io.netty", "netty-handler").versionAsInProject(), // mavenBundle("io.netty", "netty-codec").versionAsInProject(), // mavenBundle("io.netty", "netty-buffer").versionAsInProject(), // mavenBundle("io.netty", "netty-transport").versionAsInProject(), // mavenBundle("io.netty", "netty-common").versionAsInProject(), // - + mavenBundle("org.opendaylight.controller.thirdparty", "exificient", "0.9.2-SNAPSHOT"), // - + mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.xerces", "2.11.0_1"), mavenBundle("org.eclipse.birt.runtime.3_7_1", "org.apache.xml.resolver", "1.2.0"), - + mavenBundle(CONTROLLER, "config-netconf-connector").versionAsInProject(), // mavenBundle(CONTROLLER, "netconf-impl").versionAsInProject(), // - + mavenBundle(CONTROLLER, "config-persister-file-adapter").versionAsInProject().noStart()); - + } public static Option bindingAwareSalBundles() { @@ -112,10 +112,12 @@ public class TestHelper { systemProperty("netconf.tcp.address").value("0.0.0.0"), // systemProperty("netconf.tcp.port").value("18383"), // - systemProperty("netconf.config.persister.storageAdapterClass").value( + systemProperty("netconf.config.persister.active").value("1"), // + systemProperty("netconf.config.persister.1.storageAdapterClass").value( "org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter"), // - systemProperty("fileStorage").value(PathUtils.getBaseDir() + "/src/test/resources/controller.config"), // - systemProperty("numberOfBackups").value("1") // + systemProperty("netconf.config.persister.1.properties.fileStorage") + .value(PathUtils.getBaseDir() + "/src/test/resources/controller.config"), // + systemProperty("netconf.config.persister.1.properties.numberOfBackups").value("1") // //systemProperty("yangstore.blacklist").value(".*controller.model.*") // ); @@ -170,7 +172,7 @@ public class TestHelper { * org/mockito/cglib/proxy/Factory have different Class objects * for the type org/mockito/cglib/ proxy/Callback used in the * signature - * + * * So we disable the bootdelegation. this property has no effect * on the other OSGi implementation. */ diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java index 351ae6ebbe..36b46a171c 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java @@ -47,13 +47,27 @@ class JsonMapper { checkNotNull(parent); checkNotNull(parentSchema); + List longestPathToElementViaChoiceCase = new ArrayList<>(); for (Node child : parent.getChildren()) { - DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes()); - if (childSchema == null) { - throw new UnsupportedDataTypeException("Probably the data node \"" + child.getNodeType().getLocalName() - + "\" is not conform to schema"); + Deque choiceCasePathStack = new ArrayDeque<>(longestPathToElementViaChoiceCase); + SchemaLocation schemaLocation = findFirstSchemaForNode(child, parentSchema.getChildNodes(), + choiceCasePathStack); + + if (schemaLocation == null) { + if (!choiceCasePathStack.isEmpty()) { + throw new UnsupportedDataTypeException("On choice-case path " + choiceCasePathStack + + " wasn't found data schema for " + child.getNodeType().getLocalName()); + } else { + throw new UnsupportedDataTypeException("Probably the data node \"" + + child.getNodeType().getLocalName() + "\" is not conform to schema"); + } } + longestPathToElementViaChoiceCase = resolveLongerPath(longestPathToElementViaChoiceCase, + schemaLocation.getLocation()); + + DataSchemaNode childSchema = schemaLocation.getSchema(); + if (childSchema instanceof ContainerSchemaNode) { Preconditions.checkState(child instanceof CompositeNode, "Data representation of Container should be CompositeNode - " + child.getNodeType()); @@ -83,7 +97,10 @@ class JsonMapper { } for (Node child : parent.getChildren()) { - DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes()); + SchemaLocation schemaLocation = findFirstSchemaForNode(child, parentSchema.getChildNodes(), + new ArrayDeque<>(longestPathToElementViaChoiceCase)); + + DataSchemaNode childSchema = schemaLocation.getSchema(); if (childSchema instanceof LeafListSchemaNode) { foundLeafLists.remove((LeafListSchemaNode) childSchema); } else if (childSchema instanceof ListSchemaNode) { @@ -92,10 +109,45 @@ class JsonMapper { } } - private DataSchemaNode findFirstSchemaForNode(Node node, Set dataSchemaNode) { + private List resolveLongerPath(List l1, List l2) { + return l1.size() > l2.size() ? l1 : l2; + } + + private SchemaLocation findFirstSchemaForNode(Node node, Set dataSchemaNode, + Deque pathIterator) { + Map choiceSubnodes = new HashMap<>(); for (DataSchemaNode dsn : dataSchemaNode) { - if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) { - return dsn; + if (dsn instanceof ChoiceNode) { + choiceSubnodes.put(dsn.getQName().getLocalName(), (ChoiceNode) dsn); + } else if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) { + return new SchemaLocation(dsn); + } + } + + for (ChoiceNode choiceSubnode : choiceSubnodes.values()) { + if ((!pathIterator.isEmpty() && pathIterator.peekLast().equals(choiceSubnode.getQName().getLocalName())) + || pathIterator.isEmpty()) { + String pathPartChoice = pathIterator.pollLast(); + for (ChoiceCaseNode concreteCase : choiceSubnode.getCases()) { + if ((!pathIterator.isEmpty() && pathIterator.peekLast().equals( + concreteCase.getQName().getLocalName())) + || pathIterator.isEmpty()) { + String pathPartCase = pathIterator.pollLast(); + SchemaLocation schemaLocation = findFirstSchemaForNode(node, concreteCase.getChildNodes(), + pathIterator); + if (schemaLocation != null) { + schemaLocation.addPathPart(concreteCase.getQName().getLocalName()); + schemaLocation.addPathPart(choiceSubnode.getQName().getLocalName()); + return schemaLocation; + } + if (pathPartCase != null) { + pathIterator.addLast(pathPartCase); + } + } + } + if (pathPartChoice != null) { + pathIterator.addLast(pathPartChoice); + } } } return null; diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/SchemaLocation.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/SchemaLocation.java new file mode 100644 index 0000000000..24055ced67 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/SchemaLocation.java @@ -0,0 +1,28 @@ +package org.opendaylight.controller.sal.rest.impl; + +import java.util.*; + +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; + +class SchemaLocation { + final private List location = new ArrayList<>(); + final private DataSchemaNode schema; + + public SchemaLocation(DataSchemaNode schema) { + this.schema = schema; + } + + DataSchemaNode getSchema() { + return schema; + } + + List getLocation() { + return location; + } + + SchemaLocation addPathPart(String partOfPath) { + location.add(partOfPath); + return this; + } + +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java index bc941b997e..89d24ad057 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java @@ -137,16 +137,22 @@ final class TestUtils { static String convertCompositeNodeDataAndYangToJson(CompositeNode compositeNode, String yangPath, String outputPath, String searchedModuleName, String searchedDataSchemaName) { - String jsonResult = null; - Set modules = null; + Set modules = resolveModules(yangPath); + Module module = resolveModule(searchedModuleName, modules); + DataSchemaNode dataSchemaNode = resolveDataSchemaNode(module, searchedDataSchemaName); try { - modules = TestUtils.loadModules(ToJsonBasicDataTypesTest.class.getResource(yangPath).getPath()); - } catch (FileNotFoundException e) { + return writeCompNodeWithSchemaContextToJson(compositeNode, outputPath, modules, dataSchemaNode); + } catch (WebApplicationException | IOException e) { + // TODO Auto-generated catch block e.printStackTrace(); } - assertNotNull("modules can't be null.", modules); + return null; + } + + static Module resolveModule(String searchedModuleName, Set modules) { + assertNotNull("modules can't be null.", modules); Module module = null; if (searchedModuleName != null) { for (Module m : modules) { @@ -158,12 +164,24 @@ final class TestUtils { } else if (modules.size() == 1) { module = modules.iterator().next(); } - assertNotNull("Module is missing", module); + return module; + } - assertNotNull("Composite node can't be null", compositeNode); + static Set resolveModules(String yangPath) { + Set modules = null; + + try { + modules = TestUtils.loadModules(ToJsonBasicDataTypesTest.class.getResource(yangPath).getPath()); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + return modules; + } + + static DataSchemaNode resolveDataSchemaNode(Module module, String searchedDataSchemaName) { + assertNotNull("Module is missing", module); - StructuredDataToJsonProvider structuredDataToJsonProvider = StructuredDataToJsonProvider.INSTANCE; - ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream(); DataSchemaNode dataSchemaNode = null; if (searchedDataSchemaName != null) { for (DataSchemaNode dsn : module.getChildNodes()) { @@ -174,19 +192,23 @@ final class TestUtils { } else if (module.getChildNodes().size() == 1) { dataSchemaNode = module.getChildNodes().iterator().next(); } + return dataSchemaNode; + } + + static String writeCompNodeWithSchemaContextToJson(CompositeNode compositeNode, String outputPath, + Set modules, DataSchemaNode dataSchemaNode) throws IOException, WebApplicationException { + String jsonResult; + assertNotNull(dataSchemaNode); - // SchemaContextUtil. + assertNotNull("Composite node can't be null", compositeNode); + ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream(); - ControllerContext controllerContext = ControllerContext.getInstance(); - controllerContext.setSchemas(loadSchemaContext(modules)); - StructuredData structuredData = new StructuredData(compositeNode, dataSchemaNode); - try { - structuredDataToJsonProvider.writeTo(structuredData, null, null, null, null, null, byteArrayOS); - } catch (WebApplicationException | IOException e) { - e.printStackTrace(); - } - assertFalse("Returning JSON string can't be empty for node " + dataSchemaNode.getQName().getLocalName(), - byteArrayOS.toString().isEmpty()); + ControllerContext contContext = ControllerContext.getInstance(); + contContext.setSchemas(loadSchemaContext(modules)); + + StructuredDataToJsonProvider structuredDataToJsonProvider = StructuredDataToJsonProvider.INSTANCE; + structuredDataToJsonProvider.writeTo(new StructuredData(compositeNode, dataSchemaNode), null, null, null, null, + null, byteArrayOS); jsonResult = byteArrayOS.toString(); try { diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ToJsonChoiceCaseTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ToJsonChoiceCaseTest.java new file mode 100644 index 0000000000..141ffdb754 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ToJsonChoiceCaseTest.java @@ -0,0 +1,131 @@ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.Set; + +import javax.activation.UnsupportedDataTypeException; +import javax.ws.rs.WebApplicationException; + +import org.junit.*; +import org.opendaylight.yangtools.yang.model.api.*; + +public class ToJsonChoiceCaseTest { + + private static Set modules; + private static DataSchemaNode dataSchemaNode; + + @BeforeClass + public static void initialization() { + modules = TestUtils.resolveModules("/yang-to-json-conversion/choice"); + Module module = TestUtils.resolveModule(null, modules); + dataSchemaNode = TestUtils.resolveDataSchemaNode(module, null); + + } + + /** + * Test when some data are in one case node and other in another. Exception + * expected!! + */ + @Test + public void compNodeDataOnVariousChoiceCasePathTest() { + boolean exceptionCatched = false; + try { + TestUtils.writeCompNodeWithSchemaContextToJson( + TestUtils.loadCompositeNode("/yang-to-json-conversion/choice/xml/data_various_path.xml"), + "/yang-to-json-conversion/choice/xml", modules, dataSchemaNode); + } catch (UnsupportedDataTypeException e) { + exceptionCatched = true; + + } catch (WebApplicationException | IOException e) { + // shouldn't end here + assertTrue(false); + } + + assertTrue(exceptionCatched); + + } + + /** + * Test when second level data are red first, then first and at the end + * third level. Level represents pass through couple choice-case + */ + @Ignore + @Test + public void compNodeDataWithRandomOrderAccordingLevel() { + try { + String jsonOutput = TestUtils.writeCompNodeWithSchemaContextToJson( + TestUtils.loadCompositeNode("/yang-to-json-conversion/choice/xml/data_random_level.xml"), + "/yang-to-json-conversion/choice/xml", modules, dataSchemaNode); + } catch (WebApplicationException | IOException e) { + // shouldn't end here + assertTrue(false); + } + } + + /** + * Test when element from no first case is used + */ + @Ignore + @Test + public void compNodeDataNoFirstCase() { + try { + String jsonOutput = TestUtils.writeCompNodeWithSchemaContextToJson( + TestUtils.loadCompositeNode("/yang-to-json-conversion/choice/xml/data_no_first_case.xml"), + "/yang-to-json-conversion/choice/xml", modules, dataSchemaNode); + } catch (WebApplicationException | IOException e) { + // shouldn't end here + assertTrue(false); + } + } + + /** + * Test when element in case is list + */ + @Ignore + @Test + public void compNodeDataAsList() { + try { + String jsonOutput = TestUtils.writeCompNodeWithSchemaContextToJson( + TestUtils.loadCompositeNode("/yang-to-json-conversion/choice/xml/data_list.xml"), + "/yang-to-json-conversion/choice/xml", modules, dataSchemaNode); + } catch (WebApplicationException | IOException e) { + // shouldn't end here + assertTrue(false); + } + } + + /** + * Test when element in case is container + */ + @Ignore + @Test + public void compNodeDataAsContainer() { + try { + String jsonOutput = TestUtils.writeCompNodeWithSchemaContextToJson( + TestUtils.loadCompositeNode("/yang-to-json-conversion/choice/xml/data_container.xml"), + "/yang-to-json-conversion/choice/xml", modules, dataSchemaNode); + } catch (WebApplicationException | IOException e) { + // shouldn't end here + assertTrue(false); + } + } + + /** + * Test when element in case is container + */ + @Ignore + @Test + public void compNodeDataAsLeafList() { + try { + String jsonOutput = TestUtils.writeCompNodeWithSchemaContextToJson( + TestUtils.loadCompositeNode("/yang-to-json-conversion/choice/xml/data_leaflist.xml"), + "/yang-to-json-conversion/choice/xml", modules, dataSchemaNode); + } catch (WebApplicationException | IOException e) { + // shouldn't end here + assertTrue(false); + } + } + +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/choice.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/choice.yang new file mode 100644 index 0000000000..8b02a92979 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/choice.yang @@ -0,0 +1,97 @@ +module choice-case-test { + namespace "choice:case:test"; + + prefix "chcatst"; + revision 2013-11-27 { + } + + container cont { + leaf lf1 { + type string; + } + + choice choi1 { + case a1 { + leaf lf1a { + type uint16; + } + choice choi1a { + case aa1 { + leaf lf1aa { + type string; + } + choice choi1aa { + case aaa1 { + leaf lf1aaa { + type string; + } + } + case aab1 { + leaf lf1aab { + type string; + } + } + } + } + case ab1 { + leaf lf1ab { + type string; + } + } + } + } + case b1 { + list lst1b { + leaf lf11b { + type string; + } + } + } + case c1 { + container cont1c { + leaf lf11c { + type string; + } + } + } + case d1 { + leaf-list lflst1d { + type string; + } + } + } + + choice choi2 { + case a2 { + leaf lf2a { + type string; + } + } + case b2 { + leaf lf2b { + type string; + } + } + } + +/* equal identifiers in various cases are illegal 7.9.2 rfc6020 */ +/* + choice choi3 { + case 3a { + leaf lf3a { + type string; + } + } + case 3b { + leaf lf3b { + type string; + } + } + } +*/ + + } + + + +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_container.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_container.xml new file mode 100644 index 0000000000..9c751949d0 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_container.xml @@ -0,0 +1,5 @@ + + + lf11c val + + \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_leaflist.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_leaflist.xml new file mode 100644 index 0000000000..6cebb6424a --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_leaflist.xml @@ -0,0 +1,4 @@ + + lflst1d_1 val + lflst1d_2 val + \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_list.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_list.xml new file mode 100644 index 0000000000..710da55de6 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_list.xml @@ -0,0 +1,8 @@ + + + lf11b_1 val + + + lf11b_2 val + + \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_more_choices_same_level.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_more_choices_same_level.xml new file mode 100644 index 0000000000..9c751949d0 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_more_choices_same_level.xml @@ -0,0 +1,5 @@ + + + lf11c val + + \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_no_first_case.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_no_first_case.xml new file mode 100644 index 0000000000..43e9974a2c --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_no_first_case.xml @@ -0,0 +1,5 @@ + + lf1 val + 121 + lf1ab val + \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_random_level.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_random_level.xml new file mode 100644 index 0000000000..b1b78e4744 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_random_level.xml @@ -0,0 +1,6 @@ + + lf1aa val + lf1 val + 121 + lf1aaa val + \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_various_path.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_various_path.xml new file mode 100644 index 0000000000..c43dab60c0 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/choice/xml/data_various_path.xml @@ -0,0 +1,6 @@ + + lf1aa val + lf1 val + 121 + lf1ab value + \ No newline at end of file diff --git a/opendaylight/netconf/config-persister-impl/pom.xml b/opendaylight/netconf/config-persister-impl/pom.xml index 21ecd31339..ce25de215e 100644 --- a/opendaylight/netconf/config-persister-impl/pom.xml +++ b/opendaylight/netconf/config-persister-impl/pom.xml @@ -53,6 +53,11 @@ ${bgpcep.version} test + + commons-io + commons-io + test + @@ -72,7 +77,6 @@ javax.management, javax.xml.parsers, org.opendaylight.controller.config.persist.api, - org.opendaylight.controller.config.persist.api.storage, org.opendaylight.controller.netconf.api, org.opendaylight.controller.netconf.api.jmx, org.opendaylight.controller.netconf.client, diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java new file mode 100644 index 0000000000..d9c5dfaded --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.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.netconf.persist.impl; + +import com.google.common.annotations.VisibleForTesting; +import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkState; + +public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHolder { + private static final Logger logger = LoggerFactory.getLogger(CapabilityStrippingConfigSnapshotHolder.class); + + private final String configSnapshot; + private final StripCapabilitiesResult stripCapabilitiesResult; + + public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set capabilities, Pattern ignoredMissingCapabilityRegex) { + final XmlElement configElement = XmlElement.fromDomElement(snapshot); + configSnapshot = XmlUtil.toString(configElement.getDomElement()); + stripCapabilitiesResult = stripCapabilities(configElement, capabilities, ignoredMissingCapabilityRegex); + } + + private static class StripCapabilitiesResult { + private final SortedSet requiredCapabilities, missingNamespaces; + + private StripCapabilitiesResult(SortedSet requiredCapabilities, SortedSet missingNamespaces) { + this.requiredCapabilities = Collections.unmodifiableSortedSet(requiredCapabilities); + this.missingNamespaces = Collections.unmodifiableSortedSet(missingNamespaces); + } + } + + + @VisibleForTesting + static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set allCapabilitiesFromHello, + Pattern ignoredMissingCapabilityRegex) { + // collect all namespaces + Set foundNamespacesInXML = getNamespaces(configElement); + logger.trace("All capabilities {}\nFound namespaces in XML {}", allCapabilitiesFromHello, foundNamespacesInXML); + // required are referenced both in xml and hello + SortedSet requiredCapabilities = new TreeSet<>(); + // can be removed + Set obsoleteCapabilities = new HashSet<>(); + // are in xml but not in hello + SortedSet missingNamespaces = new TreeSet<>(foundNamespacesInXML); + for (String capability : allCapabilitiesFromHello) { + String namespace = capability.replaceAll("\\?.*",""); + if (foundNamespacesInXML.contains(namespace)) { + requiredCapabilities.add(capability); + checkState(missingNamespaces.remove(namespace)); + } else { + obsoleteCapabilities.add(capability); + } + } + + logger.trace("Required capabilities {}, \nObsolete capabilities {}", + requiredCapabilities, obsoleteCapabilities); + + for(Iterator iterator = missingNamespaces.iterator();iterator.hasNext(); ){ + String capability = iterator.next(); + if (ignoredMissingCapabilityRegex.matcher(capability).matches()){ + logger.trace("Ignoring missing capability {}", capability); + iterator.remove(); + } + } + if (missingNamespaces.size() > 0) { + logger.warn("Some capabilities are missing: {}", missingNamespaces); + } + return new StripCapabilitiesResult(requiredCapabilities, missingNamespaces); + } + + static Set getNamespaces(XmlElement element){ + Set result = new HashSet<>(); + for (Entry attribute : element.getAttributes().entrySet()) { + if (attribute.getKey().startsWith("xmlns")){ + result.add(attribute.getValue().getValue()); + } + } + //element.getAttributes() + for(XmlElement child: element.getChildElements()) { + result.addAll(getNamespaces(child)); + } + return result; + } + + @Override + public SortedSet getCapabilities() { + return stripCapabilitiesResult.requiredCapabilities; + } + + @VisibleForTesting + Set getMissingNamespaces(){ + return stripCapabilitiesResult.missingNamespaces; + } + + @Override + public String getConfigSnapshot() { + return configSnapshot; + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java index 25d2ad6abd..a569f90538 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 @@ -13,6 +13,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; +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; @@ -43,6 +44,7 @@ import java.net.InetSocketAddress; import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.regex.Pattern; /** * Responsible for listening for notifications from netconf containing latest @@ -53,6 +55,8 @@ import java.util.Set; 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; @@ -66,26 +70,29 @@ public class ConfigPersisterNotificationHandler implements NotificationListener, private final ObjectName on = DefaultCommitOperationMXBean.objectName; - public static final long DEFAULT_TIMEOUT = 40000L; + public static final long DEFAULT_TIMEOUT = 120000L;// 120 seconds until netconf must be stable private final long timeout; + private final Pattern ignoredMissingCapabilityRegex; public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address, - MBeanServerConnection mbeanServer) { - this(persister, address, mbeanServer, DEFAULT_TIMEOUT); + MBeanServerConnection mbeanServer, Pattern ignoredMissingCapabilityRegex) { + this(persister, address, mbeanServer, DEFAULT_TIMEOUT, ignoredMissingCapabilityRegex); + } public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address, - MBeanServerConnection mbeanServer, long timeout) { + MBeanServerConnection mbeanServer, long timeout, Pattern ignoredMissingCapabilityRegex) { this.persister = persister; this.address = address; this.mbeanServer = mbeanServer; this.timeout = timeout; this.nettyThreadgroup = new NioEventLoopGroup(); + this.ignoredMissingCapabilityRegex = ignoredMissingCapabilityRegex; } public void init() throws InterruptedException { - Optional maybeConfig = loadLastConfig(); + Optional maybeConfig = loadLastConfig(); if (maybeConfig.isPresent()) { logger.debug("Last config found {}", persister); @@ -93,6 +100,7 @@ public class ConfigPersisterNotificationHandler implements NotificationListener, registerToNetconf(maybeConfig.get().getCapabilities()); final String configSnapshot = maybeConfig.get().getConfigSnapshot(); + logger.trace("Pushing following xml to netconf {}", configSnapshot); try { pushLastConfig(XmlUtil.readXmlToElement(configSnapshot)); } catch (SAXException | IOException e) { @@ -219,26 +227,16 @@ public class ConfigPersisterNotificationHandler implements NotificationListener, private void handleAfterCommitNotification(final CommitJMXNotification notification) { try { - final XmlElement configElement = XmlElement.fromDomElement(notification.getConfigSnapshot()); - persister.persistConfig(new Persister.ConfigSnapshotHolder() { - @Override - public String getConfigSnapshot() { - return XmlUtil.toString(configElement.getDomElement()); - } - - @Override - public Set getCapabilities() { - return notification.getCapabilities(); - } - }); + persister.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(), + notification.getCapabilities(), ignoredMissingCapabilityRegex)); logger.debug("Configuration persisted successfully"); } catch (IOException e) { throw new RuntimeException("Unable to persist configuration snapshot", e); } } - private Optional loadLastConfig() { - Optional maybeConfigElement; + private Optional loadLastConfig() { + Optional maybeConfigElement; try { maybeConfigElement = persister.loadLastConfig(); } catch (IOException e) { @@ -247,12 +245,15 @@ public class ConfigPersisterNotificationHandler implements NotificationListener, return maybeConfigElement; } - private synchronized void pushLastConfig(Element persistedConfig) { + private synchronized void pushLastConfig(Element xmlToBePersisted) { + logger.info("Pushing last configuration to netconf"); StringBuilder response = new StringBuilder("editConfig response = {"); - Element configElement = persistedConfig; - NetconfMessage message = createEditConfigMessage(configElement, "/netconfOp/editConfig.xml"); - NetconfMessage responseMessage = netconfClient.sendMessage(message); + + 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)); @@ -261,7 +262,7 @@ public class ConfigPersisterNotificationHandler implements NotificationListener, checkIsOk(element, responseMessage); response.append(XmlUtil.toString(responseMessage.getDocument())); response.append("}"); - responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml")); + 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)); @@ -271,7 +272,8 @@ public class ConfigPersisterNotificationHandler implements NotificationListener, response.append("commit response = {"); response.append(XmlUtil.toString(responseMessage.getDocument())); response.append("}"); - logger.debug("Last configuration loaded successfully"); + logger.info("Last configuration loaded successfully"); + logger.trace("Detailed message {}", response); } private void checkIsOk(XmlElement element, NetconfMessage responseMessage) { @@ -289,8 +291,8 @@ public class ConfigPersisterNotificationHandler implements NotificationListener, } } - private NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) { - try (InputStream stream = getClass().getResourceAsStream(editConfigResourcename)) { + private static NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) { + try (InputStream stream = ConfigPersisterNotificationHandler.class.getResourceAsStream(editConfigResourcename)) { Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcename); Document doc = XmlUtil.readXmlToDocument(stream); diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java index cd604312f4..b37c1457c3 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 @@ -9,19 +9,22 @@ package org.opendaylight.controller.netconf.persist.impl; import com.google.common.base.Optional; -import org.opendaylight.controller.config.persist.api.storage.StorageAdapter; -import org.osgi.framework.BundleContext; +import org.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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -public class NoOpStorageAdapter implements StorageAdapter { +public class NoOpStorageAdapter implements StorageAdapter, Persister { private static final Logger logger = LoggerFactory.getLogger(NoOpStorageAdapter.class); @Override - public void setProperties(BundleContext bundleContext) { - logger.debug("setProperties called with {}", bundleContext); + public Persister instantiate(PropertiesProvider propertiesProvider) { + logger.debug("instantiate called with {}", propertiesProvider); + return this; } @Override @@ -36,7 +39,7 @@ public class NoOpStorageAdapter implements StorageAdapter { } @Override - public void close() throws IOException { + public void close() { logger.debug("close called"); } } 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 new file mode 100644 index 0000000000..7add448025 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregator.java @@ -0,0 +1,193 @@ +/* + * 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.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; +import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator; +import org.opendaylight.controller.netconf.persist.impl.osgi.PropertiesProviderBaseImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +/** + * {@link Persister} implementation that delegates persisting functionality to + * underlying {@link Persister} storages. Each storage has unique id, class, readonly value. + * + * Storage adapters are low level persisters that do the heavy lifting for this + * class. Instances of storage adapters can be injected directly via constructor + * or instantiated from a full name of its class provided in a properties file. + * + * Example configuration:
+ netconf.config.persister.active=2,3
+ # read startup configuration
+ netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.DirectoryStorageAdapter
+ netconf.config.persister.1.properties.fileStorage=configuration/initial/
+
+ netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
+ netconf.config.persister.2.readonly=true
+ netconf.config.persister.2.properties.fileStorage=configuration/current/controller.config.1.txt
+
+ netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
+ netconf.config.persister.3.properties.fileStorage=configuration/current/controller.config.2.txt
+ netconf.config.persister.3.properties.numberOfBackups=3
+
+ 
+ * 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 + * specified (first '3', then '2'). + * + * When a commit notification is received, '2' will be omitted because readonly flag is set to true, so + * only '3' will have a chance to persist new configuration. If readonly was false or not specified, both storage adapters + * would be called in order specified by 'netconf.config.persister' property. + * + */ +public final class PersisterAggregator implements Persister { + private static final Logger logger = LoggerFactory.getLogger(PersisterAggregator.class); + + public static class PersisterWithConfiguration { + + public final Persister storage; + private final boolean readOnly; + + public PersisterWithConfiguration(Persister storage, boolean readOnly) { + this.storage = storage; + this.readOnly = readOnly; + } + + @Override + public String toString() { + return "PersisterWithConfiguration{" + + "storage=" + storage + + ", readOnly=" + readOnly + + '}'; + } + } + + private static PersisterWithConfiguration loadConfiguration(final String index, final PropertiesProviderBaseImpl propertiesProvider) { + + String classKey = index + "." + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX; + String storageAdapterClass = propertiesProvider.getProperty(classKey); + StorageAdapter storageAdapter; + if (storageAdapterClass == null || storageAdapterClass.equals("")) { + throw new IllegalStateException("No persister is defined in " + + propertiesProvider.getFullKeyForReporting(classKey) + + " property. Persister is not operational"); + } + + try { + Class clazz = Class.forName(storageAdapterClass); + boolean implementsCorrectIfc = StorageAdapter.class.isAssignableFrom(clazz); + if (implementsCorrectIfc == false) { + throw new IllegalArgumentException("Storage adapter " + clazz + " does not implement " + StorageAdapter.class); + } + storageAdapter = StorageAdapter.class.cast(clazz.newInstance()); + + boolean readOnly = false; + String readOnlyProperty = propertiesProvider.getProperty(index + "." + "readonly"); + if (readOnlyProperty != null && readOnlyProperty.equals("true")) { + readOnly = true; + } + + PropertiesProviderAdapterImpl innerProvider = new PropertiesProviderAdapterImpl(propertiesProvider, index); + Persister storage = storageAdapter.instantiate(innerProvider); + return new PersisterWithConfiguration(storage, readOnly); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + throw new IllegalArgumentException("Unable to instantiate storage adapter from " + storageAdapterClass, e); + } + } + + /** + * Persisters ordered by 'netconf.config.persister' property. + */ + private final List persisterWithConfigurations; + + public PersisterAggregator(List persisterWithConfigurations) { + this.persisterWithConfigurations = persisterWithConfigurations; + + } + + public static PersisterAggregator createFromProperties(PropertiesProviderBaseImpl propertiesProvider) { + List persisterWithConfigurations = new ArrayList<>(); + String prefixes = propertiesProvider.getProperty("active"); + if (prefixes.isEmpty() == false) { + String [] keys = prefixes.split(","); + for (String index: keys) { + persisterWithConfigurations.add(PersisterAggregator.loadConfiguration(index, propertiesProvider)); + } + } + logger.debug("Initialized persister with following adapters {}", persisterWithConfigurations); + return new PersisterAggregator(persisterWithConfigurations); + } + + @Override + public void persistConfig(ConfigSnapshotHolder holder) throws IOException { + for (PersisterWithConfiguration persisterWithConfiguration: persisterWithConfigurations){ + if (!persisterWithConfiguration.readOnly){ + logger.debug("Calling {}.persistConfig",persisterWithConfiguration.storage); + persisterWithConfiguration.storage.persistConfig(holder); + } + } + } + + @Override + public Optional loadLastConfig() throws IOException { + // 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; + } + } + // no storage had an answer + return Optional.absent(); + } + + @VisibleForTesting + List getPersisterWithConfigurations() { + return persisterWithConfigurations; + } + + @Override + public void close() { + RuntimeException lastException = null; + for (PersisterWithConfiguration persisterWithConfiguration: persisterWithConfigurations){ + try{ + persisterWithConfiguration.storage.close(); + }catch(RuntimeException e) { + logger.error("Error while closing {}", persisterWithConfiguration.storage, e); + if (lastException == null){ + lastException = e; + } else { + lastException.addSuppressed(e); + } + } + } + if (lastException != null){ + throw lastException; + } + } + + @Override + public String toString() { + return "PersisterAggregator{" + + "persisterWithConfigurations=" + persisterWithConfigurations + + '}'; + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java deleted file mode 100644 index e06968e868..0000000000 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java +++ /dev/null @@ -1,96 +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.netconf.persist.impl; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; -import org.opendaylight.controller.config.persist.api.Persister; -import org.opendaylight.controller.config.persist.api.storage.StorageAdapter; -import org.osgi.framework.BundleContext; - -import java.io.IOException; - -/** - * {@link Persister} implementation that delegates persisting functionality to - * underlying {@link Persister} called Storage Adapter. - * - * Storage adapters are low level persisters that do the heavy lifting for this - * class. Instances of storage adapters can be injected directly via constructor - * or instantiated from a full name of its class provided in a properties file. - * - * Name of storage adapter class should be located under - * {@link #STORAGE_ADAPTER_CLASS_PROP} key. - */ -public final class PersisterImpl implements Persister { - - public static final String STORAGE_ADAPTER_CLASS_PROP = "netconf.config.persister.storageAdapterClass"; - private final StorageAdapter storage; - - public PersisterImpl(StorageAdapter storage) { - this.storage = storage; - } - - public static Optional createFromProperties(BundleContext bundleContext) { - String storageAdapterClass = bundleContext.getProperty(STORAGE_ADAPTER_CLASS_PROP); - StorageAdapter storage; - if (storageAdapterClass == null || storageAdapterClass.equals("")) { - return Optional.absent(); - } - - try { - storage = StorageAdapter.class.cast(resolveClass(storageAdapterClass, StorageAdapter.class).newInstance()); - storage.setProperties(bundleContext); - - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { - throw new IllegalArgumentException("Unable to instantiate storage adapter from " + storageAdapterClass, e); - } - return Optional.of(new PersisterImpl(storage)); - } - - private static Class resolveClass(String storageAdapterClass, Class baseType) throws ClassNotFoundException { - Class clazz = Class.forName(storageAdapterClass); - - if (!isImplemented(baseType, clazz)) - throw new IllegalArgumentException("Storage adapter " + clazz + " has to implement " + baseType); - return clazz; - } - - private static boolean isImplemented(Class expectedIface, Class byClazz) { - for (Class iface : byClazz.getInterfaces()) { - if (iface.equals(expectedIface)) - return true; - } - return false; - } - - @Override - public void persistConfig(ConfigSnapshotHolder holder) throws IOException { - storage.persistConfig(holder); - } - - @Override - public Optional loadLastConfig() throws IOException { - return storage.loadLastConfig(); - } - - @VisibleForTesting - StorageAdapter getStorage() { - return storage; - } - - @Override - public void close() throws IOException { - storage.close(); - } - - @Override - public String toString() { - return "PersisterImpl [storage=" + storage + "]"; - } -} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PropertiesProviderAdapterImpl.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PropertiesProviderAdapterImpl.java new file mode 100644 index 0000000000..981be827b7 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PropertiesProviderAdapterImpl.java @@ -0,0 +1,37 @@ +/** + * @author Tomas Olvecky + * + * 11 2013 + * + * Copyright (c) 2013 by Cisco Systems, Inc. + * All rights reserved. + */ +package org.opendaylight.controller.netconf.persist.impl; + +import org.opendaylight.controller.config.persist.api.PropertiesProvider; +import org.opendaylight.controller.netconf.persist.impl.osgi.PropertiesProviderBaseImpl; + +public class PropertiesProviderAdapterImpl implements PropertiesProvider { + private final PropertiesProviderBaseImpl inner; + private final String index; + + public PropertiesProviderAdapterImpl(PropertiesProviderBaseImpl inner, String index) { + this.inner = inner; + this.index = index; + } + + @Override + public String getProperty(String key) { + String fullKey = getFullKeyForReporting(key); + return inner.getPropertyWithoutPrefix(fullKey); + } + + public String getPrefix() { + return inner.getPrefix() + "." + index + ".properties"; + } + + @Override + public String getFullKeyForReporting(String key) { + return getPrefix() + "." + key; + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java index ae6c95312c..5fa0b49d7b 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java @@ -8,12 +8,9 @@ package org.opendaylight.controller.netconf.persist.impl.osgi; -import com.google.common.base.Optional; import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler; -import org.opendaylight.controller.netconf.persist.impl.NoOpStorageAdapter; -import org.opendaylight.controller.netconf.persist.impl.PersisterImpl; +import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator; import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; -import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.TLSConfiguration; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.slf4j.Logger; @@ -22,43 +19,45 @@ import org.slf4j.LoggerFactory; import javax.management.MBeanServer; import java.lang.management.ManagementFactory; import java.net.InetSocketAddress; +import java.util.regex.Pattern; public class ConfigPersisterActivator implements BundleActivator { private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class); private final static MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer(); + private static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex"; private ConfigPersisterNotificationHandler configPersisterNotificationHandler; private Thread initializationThread; - @Override - public void start(BundleContext context) throws Exception { - logger.debug("ConfigPersister activator started"); + 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"; - Optional maybePersister = PersisterImpl.createFromProperties(context); - if (maybePersister.isPresent() == false) { - throw new IllegalStateException("No persister is defined in " + PersisterImpl.STORAGE_ADAPTER_CLASS_PROP - + " property. For noop persister use " + NoOpStorageAdapter.class.getCanonicalName() - + " . Persister is not operational"); - } + @Override + public void start(final BundleContext context) throws Exception { + logger.debug("ConfigPersister starting"); - Optional maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(context); - Optional maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(context); + PropertiesProviderBaseImpl propertiesProvider = new PropertiesProviderBaseImpl(context); - InetSocketAddress address; - if (maybeTLSConfiguration.isPresent()) { - throw new UnsupportedOperationException("TLS is currently not supported for persister"); - } else if (maybeTCPAddress.isPresent()) { - address = maybeTCPAddress.get(); + String regexProperty = propertiesProvider.getProperty(IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX); + String regex; + if (regexProperty != null) { + regex = regexProperty; } else { - throw new IllegalStateException("Netconf is not configured, persister is not operational"); + regex = DEFAULT_IGNORED_REGEX; } + Pattern ignoredMissingCapabilityRegex = Pattern.compile(regex); + PersisterAggregator persister = PersisterAggregator.createFromProperties(propertiesProvider); - PersisterImpl persister = maybePersister.get(); + InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context, + "Netconf is not configured, persister is not operational"); configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(persister, address, - platformMBeanServer); + platformMBeanServer, ignoredMissingCapabilityRegex); + + // offload initialization to another thread in order to stop blocking activator Runnable initializationRunnable = new Runnable() { @Override public void run() { diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/PropertiesProviderBaseImpl.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/PropertiesProviderBaseImpl.java new file mode 100644 index 0000000000..15ed5c48fa --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/PropertiesProviderBaseImpl.java @@ -0,0 +1,44 @@ +/** + * @author Tomas Olvecky + * + * 11 2013 + * + * Copyright (c) 2013 by Cisco Systems, Inc. + * All rights reserved. + */ +package org.opendaylight.controller.netconf.persist.impl.osgi; + +import org.opendaylight.controller.config.persist.api.PropertiesProvider; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PropertiesProviderBaseImpl implements PropertiesProvider { + + private static final Logger logger = LoggerFactory.getLogger(PropertiesProviderBaseImpl.class); + private final BundleContext bundleContext; + + public PropertiesProviderBaseImpl(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + @Override + public String getProperty(String key) { + String fullKey = getFullKeyForReporting(key); + return getPropertyWithoutPrefix(fullKey); + } + + public String getPropertyWithoutPrefix(String fullKey){ + logger.trace("Full key {}", fullKey); + return bundleContext.getProperty(fullKey); + } + + public String getPrefix(){ + return ConfigPersisterActivator.NETCONF_CONFIG_PERSISTER; + } + + @Override + public String getFullKeyForReporting(String key) { + return getPrefix() + "." + key; + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java new file mode 100644 index 0000000000..d91712f347 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.persist.impl; + +import com.google.common.collect.Sets; +import org.apache.commons.io.IOUtils; +import org.junit.Test; +import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Element; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertEquals; + +public class CapabilityStrippingConfigSnapshotHolderTest { + + @Test + public void testCapabilityStripping() throws Exception { + Set allCapabilities = readLines("/capabilities-all.txt"); + Set expectedCapabilities = readLines("/capabilities-stripped.txt"); + String snapshotAsString = readToString("/snapshot.xml"); + Element element = XmlUtil.readXmlToElement(snapshotAsString); + { + CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder( + element, allCapabilities, Pattern.compile( + ConfigPersisterActivator.DEFAULT_IGNORED_REGEX + )); + assertEquals(expectedCapabilities, tested.getCapabilities()); + assertEquals(Collections.emptySet(), tested.getMissingNamespaces()); + } + { + // test regex + CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder( + element, allCapabilities, Pattern.compile( + "^bar" + )); + assertEquals(expectedCapabilities, tested.getCapabilities()); + assertEquals(Sets.newHashSet(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX.substring(1)), + tested.getMissingNamespaces()); + } + } + + private Set readLines(String fileName) throws IOException { + return new HashSet<>(IOUtils.readLines(getClass().getResourceAsStream(fileName))); + } + + private String readToString(String fileName) throws IOException { + return IOUtils.toString(getClass().getResourceAsStream(fileName)); + } + +} 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 new file mode 100644 index 0000000000..1b4031ac57 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregatorTest.java @@ -0,0 +1,194 @@ +/* + * 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 org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +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 org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter; +import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator; +import org.opendaylight.controller.netconf.persist.impl.osgi.PropertiesProviderBaseImpl; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.matchers.JUnitMatchers.containsString; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class PersisterAggregatorTest { + @Mock + TestingPropertiesProvider propertiesProvider; + + class TestingPropertiesProvider extends PropertiesProviderBaseImpl { + TestingPropertiesProvider() { + super(null); + } + + @Override + public String getFullKeyForReporting(String key) { + return "prefix." + key; + } + + @Override + public String getProperty(String key) { + throw new UnsupportedOperationException("should be mocked"); + } + } + + @Before + public void setUpMocks() { + MockitoAnnotations.initMocks(this); + doCallRealMethod().when(propertiesProvider).getFullKeyForReporting(anyString()); + } + + @Ignore + @Test + public void testFromProperties() throws Exception { + doReturn("").when(propertiesProvider).getProperty(ConfigPersisterActivator.NETCONF_CONFIG_PERSISTER); + doReturn(MockAdapter.class.getName()).when(propertiesProvider).getProperty( + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX); + doReturn("false").when(propertiesProvider).getProperty("readOnly"); + + PersisterAggregator persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider); + persisterAggregator.persistConfig(null); + persisterAggregator.loadLastConfig(); + persisterAggregator.persistConfig(null); + persisterAggregator.loadLastConfig(); + + assertEquals(2, MockAdapter.persist); + assertEquals(2, MockAdapter.load); + assertEquals(1, MockAdapter.props); + } + + + @Ignore + @Test + public void testFromProperties2() throws Exception { + String prefix = ""; + doReturn(prefix).when(propertiesProvider).getProperty(ConfigPersisterActivator.NETCONF_CONFIG_PERSISTER); + doReturn(FileStorageAdapter.class.getName()).when(propertiesProvider).getProperty( + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX); + + doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when( + propertiesProvider).getProperty("prefix.properties.fileStorage"); + doReturn("propertiesProvider").when(propertiesProvider).toString(); + doReturn(null).when(propertiesProvider).getProperty("prefix.properties.numberOfBackups"); + + PersisterAggregator persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider); + } + + @Ignore + @Test + public void testFromProperties3() throws Exception { + doReturn("").when(propertiesProvider).getProperty(ConfigPersisterActivator.NETCONF_CONFIG_PERSISTER); + doReturn(FileStorageAdapter.class.getName()).when(propertiesProvider).getProperty( + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX); + doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when( + propertiesProvider).getProperty("prefix.properties.fileStorage"); + doReturn("false").when(propertiesProvider).getProperty("readOnly"); + doReturn("propertiesProvider").when(propertiesProvider).toString(); + doReturn("0").when(propertiesProvider).getProperty("prefix.properties.numberOfBackups"); + try { + PersisterAggregator.createFromProperties(propertiesProvider); + fail(); + } catch (RuntimeException e) { + assertThat( + e.getMessage(), + containsString("numberOfBackups property should be either set to positive value, or ommited. Can not be set to 0.")); + } + } + + @Test + public void loadLastConfig() throws Exception { + List persisterWithConfigurations = new ArrayList<>(); + PersisterAggregator.PersisterWithConfiguration first = new PersisterAggregator.PersisterWithConfiguration(mock(Persister.class), false); + + ConfigSnapshotHolder ignored = mock(ConfigSnapshotHolder.class); + doReturn(Optional.of(ignored)).when(first.storage).loadLastConfig(); // should be ignored + + ConfigSnapshotHolder used = mock(ConfigSnapshotHolder.class); + PersisterAggregator.PersisterWithConfiguration second = new PersisterAggregator.PersisterWithConfiguration(mock(Persister.class), false); + doReturn(Optional.of(used)).when(second.storage).loadLastConfig(); // should be used + + PersisterAggregator.PersisterWithConfiguration third = new PersisterAggregator.PersisterWithConfiguration(mock(Persister.class), false); + doReturn(Optional.absent()).when(third.storage).loadLastConfig(); + + 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()); + } + + @Ignore + @Test + public void test() throws Exception { +// Persister storage = mock(Persister.class); +// doReturn(null).when(storage).loadLastConfig(); +// doNothing().when(storage).persistConfig(any(ConfigSnapshotHolder.class)); +// +// PersisterAggregator persister = new PersisterAggregator(storage); +// persister.loadLastConfig(); +// persister.persistConfig(null); +// +// verify(storage).loadLastConfig(); +// verify(storage).persistConfig(any(ConfigSnapshotHolder.class)); + } + + public static class MockAdapter implements StorageAdapter, Persister { + + static int persist = 0; + + @Override + public void persistConfig(ConfigSnapshotHolder holder) throws IOException { + persist++; + } + + static int load = 0; + + @Override + public Optional loadLastConfig() throws IOException { + load++; + return Optional.absent(); + } + + static int props = 0; + + @Override + public Persister instantiate(PropertiesProvider propertiesProvider) { + props++; + return this; + } + + @Override + public void close() { + } + + } + +} diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java deleted file mode 100644 index 44b3b61043..0000000000 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java +++ /dev/null @@ -1,138 +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.netconf.persist.impl; - -import com.google.common.base.Optional; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.config.persist.api.Persister; -import org.opendaylight.controller.config.persist.api.storage.StorageAdapter; -import org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter; -import org.osgi.framework.BundleContext; - -import java.io.File; -import java.io.IOException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.matchers.JUnitMatchers.containsString; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class PersisterImplTest { - @Mock - BundleContext mockedContext; - - @Before - public void setUpMocks() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testFromProperties() throws Exception { - doReturn(MockAdapter.class.getName()).when(mockedContext).getProperty( - PersisterImpl.STORAGE_ADAPTER_CLASS_PROP); - - PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedContext).get(); - persisterImpl.persistConfig(null); - persisterImpl.loadLastConfig(); - persisterImpl.persistConfig(null); - persisterImpl.loadLastConfig(); - - assertEquals(2, MockAdapter.persist); - assertEquals(2, MockAdapter.load); - assertEquals(1, MockAdapter.props); - } - - @Test - public void testFromProperties2() throws Exception { - mockedContext = mock(BundleContext.class); - doReturn(FileStorageAdapter.class.getName()).when(mockedContext).getProperty( - PersisterImpl.STORAGE_ADAPTER_CLASS_PROP); - doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when( - mockedContext).getProperty(FileStorageAdapter.FILE_STORAGE_PROP); - doReturn("mockedContext").when(mockedContext).toString(); - doReturn(null).when(mockedContext).getProperty("numberOfBackups"); - - PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedContext).get(); - assertTrue(persisterImpl.getStorage() instanceof FileStorageAdapter); - } - - @Test - public void testFromProperties3() throws Exception { - mockedContext = mock(BundleContext.class); - doReturn(FileStorageAdapter.class.getName()).when(mockedContext).getProperty( - PersisterImpl.STORAGE_ADAPTER_CLASS_PROP); - doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when( - mockedContext).getProperty(FileStorageAdapter.FILE_STORAGE_PROP); - doReturn("mockedContext").when(mockedContext).toString(); - doReturn("0").when(mockedContext).getProperty("numberOfBackups"); - try { - PersisterImpl.createFromProperties(mockedContext).get(); - fail(); - } catch (RuntimeException e) { - assertThat( - e.getMessage(), - containsString("numberOfBackups property should be either set to positive value, or ommited. Can not be set to 0.")); - } - } - - @Test - public void test() throws Exception { - StorageAdapter storage = mock(StorageAdapter.class); - doReturn(null).when(storage).loadLastConfig(); - doNothing().when(storage).persistConfig(any(Persister.ConfigSnapshotHolder.class)); - PersisterImpl persister = new PersisterImpl(storage); - persister.loadLastConfig(); - persister.persistConfig(null); - - verify(storage).loadLastConfig(); - verify(storage).persistConfig(any(Persister.ConfigSnapshotHolder.class)); - } - - public static class MockAdapter implements StorageAdapter { - - static int persist = 0; - - @Override - public void persistConfig(ConfigSnapshotHolder holder) throws IOException { - persist++; - } - - static int load = 0; - - @Override - public Optional loadLastConfig() throws IOException { - load++; - return null;// ? - } - - static int props = 0; - - @Override - public void setProperties(BundleContext configProvider) { - props++; - } - - @Override - public void close() throws IOException { - // TODO Auto-generated method stub - - } - - } - -} diff --git a/opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-all.txt b/opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-all.txt new file mode 100644 index 0000000000..84c85b740c --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-all.txt @@ -0,0 +1,20 @@ +urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27 +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/netconf/config-persister-impl/src/test/resources/capabilities-stripped.txt b/opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-stripped.txt new file mode 100644 index 0000000000..4a0b7ffffd --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-stripped.txt @@ -0,0 +1,5 @@ +urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 +urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28 +urn:opendaylight:params:xml:ns:yang:controller: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:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28 diff --git a/opendaylight/netconf/config-persister-impl/src/test/resources/logback-test.xml b/opendaylight/netconf/config-persister-impl/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..a4ff3abe49 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + %date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/opendaylight/netconf/config-persister-impl/src/test/resources/snapshot.xml b/opendaylight/netconf/config-persister-impl/src/test/resources/snapshot.xml new file mode 100644 index 0000000000..a6a57d704a --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/test/resources/snapshot.xml @@ -0,0 +1,103 @@ + + + + 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 + + + + + + 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'] + + + + diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java index 890bbe7288..b334c354fb 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java @@ -16,7 +16,6 @@ import org.opendaylight.controller.netconf.impl.NetconfServerSessionListenerFact import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory; import org.opendaylight.controller.netconf.impl.SessionIdProvider; import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; -import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.TLSConfiguration; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.slf4j.Logger; @@ -30,9 +29,6 @@ public class NetconfImplActivator implements BundleActivator { private static final Logger logger = LoggerFactory.getLogger(NetconfImplActivator.class); - private Optional maybeTCPAddress; - private Optional maybeTLSConfiguration; - private NetconfOperationServiceFactoryTracker factoriesTracker; private DefaultCommitNotificationProducer commitNot; private NetconfServerDispatcher dispatch; @@ -41,11 +37,8 @@ public class NetconfImplActivator implements BundleActivator { @Override public void start(final BundleContext context) throws Exception { - maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(context); - maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(context); - if (maybeTCPAddress.isPresent() == false && maybeTLSConfiguration.isPresent() == false) { - throw new IllegalStateException("TCP nor TLS is configured, netconf not available."); - } + InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context, "TCP is not configured, netconf not available."); + NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener); factoriesTracker.open(); @@ -62,26 +55,13 @@ public class NetconfImplActivator implements BundleActivator { eventLoopGroup = new NioEventLoopGroup(); - if (maybeTCPAddress.isPresent()) { - Optional maybeSSLContext = Optional.absent(); - InetSocketAddress address = maybeTCPAddress.get(); - NetconfServerDispatcher.ServerSslChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerSslChannelInitializer( - maybeSSLContext, serverNegotiatorFactory, listenerFactory); - dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup); - - logger.info("Starting TCP netconf server at {}", address); - dispatch.createServer(address); - } - if (maybeTLSConfiguration.isPresent()) { - Optional maybeSSLContext = Optional.of(maybeTLSConfiguration.get().getSslContext()); - InetSocketAddress address = maybeTLSConfiguration.get().getAddress(); - NetconfServerDispatcher.ServerSslChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerSslChannelInitializer( - maybeSSLContext, serverNegotiatorFactory, listenerFactory); - dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup); - - logger.info("Starting TLS netconf server at {}", address); - dispatch.createServer(address); - } + NetconfServerDispatcher.ServerSslChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerSslChannelInitializer( + Optional.absent(), serverNegotiatorFactory, listenerFactory); + dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup); + + logger.info("Starting TCP netconf server at {}", address); + dispatch.createServer(address); + } @Override diff --git a/opendaylight/netconf/netconf-it/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 1037bf6922..65cc2b4e37 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 @@ -27,6 +27,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; import javax.management.ObjectName; import javax.net.ssl.SSLContext; import javax.xml.parsers.ParserConfigurationException; @@ -59,6 +60,7 @@ import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler; import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler; import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl; import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler; +import org.opendaylight.controller.netconf.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; @@ -198,8 +200,8 @@ public class NetconfITTest extends AbstractConfigTest { @Test public void testTwoSessions() throws Exception { - try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 4000, clientDispatcher)) { - try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 4000, clientDispatcher)) { + try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 10000, clientDispatcher)) { + try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 10000, clientDispatcher)) { } } } @@ -209,7 +211,8 @@ public class NetconfITTest extends AbstractConfigTest { Persister persister = mock(Persister.class); doReturn("mockPersister").when(persister).toString(); doReturn(Optional.absent()).when(persister).loadLastConfig(); - ConfigPersisterNotificationHandler h = new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer()); + ConfigPersisterNotificationHandler h = + new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer(), Pattern.compile(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX)); h.init(); } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java index 6626f47b03..d2f6c8c81c 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java @@ -37,10 +37,11 @@ public class NetconfSSHActivator implements BundleActivator{ logger.trace("Starting netconf SSH bridge."); Optional sshSocketAddressOptional = NetconfConfigUtil.extractSSHNetconfAddress(context); - Optional tcpSocketAddressOptional = NetconfConfigUtil.extractTCPNetconfAddress(context); + InetSocketAddress tcpSocketAddress = NetconfConfigUtil.extractTCPNetconfAddress(context, + "TCP is not configured, netconf ssh bridge is not available."); - if (sshSocketAddressOptional.isPresent() && tcpSocketAddressOptional.isPresent()){ - server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddressOptional.get()); + if (sshSocketAddressOptional.isPresent()){ + server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress); Thread serverThread = new Thread(server,"netconf SSH server thread"); serverThread.setDaemon(true); serverThread.start(); diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java index 8a0a9cd80e..35e17a2a3e 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java @@ -35,8 +35,13 @@ public class NetconfConfigUtil { private static final String NETCONF_TLS_KEYSTORE_PROP = PREFIX_PROP + InfixProp.tls + ".keystore"; private static final String NETCONF_TLS_KEYSTORE_PASSWORD_PROP = NETCONF_TLS_KEYSTORE_PROP + ".password"; - public static Optional extractTCPNetconfAddress(BundleContext context) { - return extractSomeNetconfAddress(context, InfixProp.tcp); + public static InetSocketAddress extractTCPNetconfAddress(BundleContext context, String exceptionMessageIfNotFound) { + + Optional inetSocketAddressOptional = extractSomeNetconfAddress(context, InfixProp.tcp); + if (inetSocketAddressOptional.isPresent() == false) { + throw new IllegalStateException("Netconf tcp address not found." + exceptionMessageIfNotFound); + } + return inetSocketAddressOptional.get(); } public static Optional extractSSHNetconfAddress(BundleContext context) {