*.iws
.idea
xtend-gen
+classes
</goals>
</pluginExecutionFilter>
<action>
- <ignore />
+ <execute>
+ <runOnIncremental>false</runOnIncremental>
+ </execute>
</action>
</pluginExecution>
<pluginExecution>
--- /dev/null
+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<String> getCapabilities();
+ }
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<ConfigSnapshotHolder> loadLastConfig() throws IOException;
- public static interface ConfigSnapshotHolder {
+ @Override
+ void close();
- String getConfigSnapshot();
-
- Set<String> getCapabilities();
- }
}
--- /dev/null
+/*
+ * 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);
+}
* 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);
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>config-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.3-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>config-persister-directory-adapter</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-persister-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
+ <plugin>
+ <groupId>org.codehaus.groovy.maven</groupId>
+ <artifactId>gmaven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>execute</goal>
+ </goals>
+ <configuration>
+ <source>
+ System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
+ </source>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
+ </Fragment-Host>
+ <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
+ </Provide-Capability>
+ <Import-Package>
+ com.google.common.base,
+ com.google.common.io,
+ org.apache.commons.io,
+ org.opendaylight.controller.config.persist.api,
+ org.slf4j
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * 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<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ File[] filesArray = storage.listFiles();
+ if (filesArray.length == 0) {
+ return Optional.absent();
+ }
+ List<File> sortedFiles = new ArrayList<>(Arrays.asList(filesArray));
+ Collections.sort(sortedFiles);
+ // combine all found files
+
+ SortedSet<String> 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<String> {
+
+ private boolean inModules, inServices, inCapabilities;
+ private final StringBuffer modulesBuffer = new StringBuffer(), servicesBuilder = new StringBuffer();
+ private final SortedSet<String> 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<String> getCapabilities() {
+ checkState(inCapabilities);
+ return caps;
+ }
+
+}
+
+class ConfigSnapshotHolderImpl implements ConfigSnapshotHolder {
+
+ private final String snapshot;
+ private final SortedSet<String> caps;
+
+ public ConfigSnapshotHolderImpl(String configSnapshot, SortedSet<String> capabilities) {
+ this.snapshot = configSnapshot;
+ this.caps = capabilities;
+ }
+
+ @Override
+ public String getConfigSnapshot() {
+ return snapshot;
+ }
+
+ @Override
+ public SortedSet<String> getCapabilities() {
+ return caps;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+ </services>
+</data>
--- /dev/null
+<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
--- /dev/null
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
--- /dev/null
+/*
+ * 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<String> 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.<ConfigSnapshotHolder>absent(), tested.loadLastConfig());
+
+ try {
+ tested.persistConfig(new ConfigSnapshotHolder() {
+ @Override
+ public String getConfigSnapshot() {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public SortedSet<String> 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<ConfigSnapshotHolder> 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();
+ }
+
+}
--- /dev/null
+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
--- /dev/null
+<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
+ <name>hash-map-data-store</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
+ <name>dom-broker</name>
+ <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <name>ref_hash-map-data-store</name>
+ </data-store>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
+ <name>binding-broker-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <name>ref_binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
+ <name>binding-notification-broker</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
+ <name>binding-data-broker</name>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>ref_dom-broker</name>
+ </dom-broker>
+ <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>ref_runtime-mapping-singleton</name>
+ </mapping-service>
+ </module>
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <instance>
+ <name>ref_yang-schema-service</name>
+ <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <instance>
+ <name>ref_binding-notification-broker</name>
+ <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <instance>
+ <name>ref_hash-map-data-store</name>
+ <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+ <instance>
+ <name>ref_binding-broker-impl</name>
+ <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
+ <instance>
+ <name>ref_runtime-mapping-singleton</name>
+ <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <instance>
+ <name>ref_dom-broker</name>
+ <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <instance>
+ <name>ref_binding-data-broker</name>
+ <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
+ </instance>
+ </service>
+ </services>
+</data>
--- /dev/null
+//MODULES START
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
+ <name>hash-map-data-store</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
+ <name>dom-broker</name>
+ <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <name>ref_hash-map-data-store</name>
+ </data-store>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
+ <name>binding-broker-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <name>ref_binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
+ <name>binding-notification-broker</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
+ <name>binding-data-broker</name>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>ref_dom-broker</name>
+ </dom-broker>
+ <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>ref_runtime-mapping-singleton</name>
+ </mapping-service>
+ </module>
+//SERVICES START
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <instance>
+ <name>ref_yang-schema-service</name>
+ <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <instance>
+ <name>ref_binding-notification-broker</name>
+ <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <instance>
+ <name>ref_hash-map-data-store</name>
+ <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+ <instance>
+ <name>ref_binding-broker-impl</name>
+ <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
+ <instance>
+ <name>ref_runtime-mapping-singleton</name>
+ <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <instance>
+ <name>ref_dom-broker</name>
+ <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <instance>
+ <name>ref_binding-data-broker</name>
+ <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
+ </instance>
+ </service>
+//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
--- /dev/null
+//MODULES START
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
+ <name>hash-map-data-store</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
+ <name>dom-broker</name>
+ <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <name>ref_hash-map-data-store</name>
+ </data-store>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
+ <name>binding-broker-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <name>ref_binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
+ <name>binding-notification-broker</name>
+ </module>
+//SERVICES START
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <instance>
+ <name>ref_yang-schema-service</name>
+ <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <instance>
+ <name>ref_binding-notification-broker</name>
+ <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <instance>
+ <name>ref_hash-map-data-store</name>
+ <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+ <instance>
+ <name>ref_binding-broker-impl</name>
+ <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
+ <instance>
+ <name>ref_runtime-mapping-singleton</name>
+ <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <instance>
+ <name>ref_dom-broker</name>
+ <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
+ </instance>
+ </service>
+//CAPABILITIES START
+urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
--- /dev/null
+//MODULES START
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
+ <name>binding-data-broker</name>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>ref_dom-broker</name>
+ </dom-broker>
+ <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>ref_runtime-mapping-singleton</name>
+ </mapping-service>
+ </module>
+//SERVICES START
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <instance>
+ <name>ref_binding-data-broker</name>
+ <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
+ </instance>
+ </service>
+//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
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,
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;
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);
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();
+ " property should be either set to positive value, or ommited. Can not be set to 0.");
}
setFileStorage(storage);
-
+ return this;
}
@VisibleForTesting
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 {
private boolean inLastConfig, inLastSnapshot;
private final StringBuffer snapshotBuffer = new StringBuffer();
- private final Set<String> caps = Sets.newHashSet();
+ private final SortedSet<String> caps = new TreeSet<>();
@Override
public String getResult() {
return Optional.of(xmlContent);
}
- Set<String> getCapabilities() throws IOException, SAXException, ParserConfigurationException {
+ SortedSet<String> getCapabilities() throws IOException, SAXException, ParserConfigurationException {
return caps;
}
}
@Override
- public void close() throws IOException {
+ public void close() {
}
private class PersistedConfigImpl implements ConfigSnapshotHolder {
private final String snapshot;
- private final Set<String> caps;
+ private final SortedSet<String> caps;
- public PersistedConfigImpl(Optional<String> configSnapshot, Set<String> capabilities) {
+ public PersistedConfigImpl(Optional<String> configSnapshot, SortedSet<String> capabilities) {
this.snapshot = configSnapshot.get();
this.caps = capabilities;
}
}
@Override
- public Set<String> getCapabilities() {
+ public SortedSet<String> getCapabilities() {
return caps;
}
}
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;
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<String> getCapabilities() {
+ public SortedSet<String> getCapabilities() {
return createCaps();
}
};
});
assertEquals(14, readLines.size());
- Optional<Persister.ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
+ Optional<ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
assertTrue(lastConf.isPresent());
assertEquals("<config>2</config>",
lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
assertEquals(createCaps(), lastConf.get().getCapabilities());
}
- private Set<String> createCaps() {
- Set<String> caps = Sets.newHashSet();
+ private SortedSet<String> createCaps() {
+ SortedSet<String> caps = new TreeSet<>();
caps.add("cap1");
caps.add("cap2");
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<String> getCapabilities() {
+ public SortedSet<String> getCapabilities() {
return createCaps();
}
};
});
assertEquals(7, readLines.size());
- Optional<Persister.ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
+ Optional<ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
assertTrue(lastConf.isPresent());
assertEquals("<config>2</config>",
lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
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<String> getCapabilities() {
+ public SortedSet<String> getCapabilities() {
return createCaps();
}
};
assertEquals(14, readLines.size());
- Optional<Persister.ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
+ Optional<ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
assertTrue(lastConf.isPresent());
assertEquals("<config>3</config>",
lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
FileStorageAdapter storage = new FileStorageAdapter();
storage.setFileStorage(file);
- Optional<Persister.ConfigSnapshotHolder> elementOptional = storage.loadLastConfig();
+ Optional<ConfigSnapshotHolder> elementOptional = storage.loadLastConfig();
assertThat(elementOptional.isPresent(), is(false));
}
@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<String> getCapabilities() {
- return Collections.<String> emptySet();
+ public SortedSet<String> getCapabilities() {
+ return new TreeSet<>();
}
} );
}
private void addNewAppenders(Map<String, Appender<ILoggingEvent>> appendersMap, LoggerTO logger,
ch.qos.logback.classic.Logger logbackLogger, Optional<Set<Appender<ILoggingEvent>>> 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");
+ }
}
}
}
"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);
+ }
}
}
<module>netty-threadgroup-config</module>
<module>netty-event-executor-config</module>
<module>netty-timer-config</module>
+ <module>config-persister-directory-adapter</module>
</modules>
<profiles>
import org.opendaylight.yangtools.sal.binding.model.api.Type;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
public class TypeProviderWrapper {
private final TypeProvider typeProvider;
}
public Type getType(LeafSchemaNode leaf) {
+ TypeDefinition<?> type = leaf.getType();
+ return getType(leaf, type);
+ }
+
+ public Type getType(SchemaNode leaf, TypeDefinition<?> type) {
Type javaType;
try {
javaType = typeProvider.javaTypeForSchemaDefinitionType(
- leaf.getType(), leaf);
+ type, leaf);
if (javaType == null)
throw new IllegalArgumentException("Unknown type received for "
+ leaf.toString());
return javaType;
}
+ public String getJMXParamForBaseType(TypeDefinition<?> baseType) {
+ return typeProvider.getConstructorPropertyName(baseType);
+ }
}
public abstract class AbstractAttribute implements AttributeIfc {
private final String attributeYangName, upperCaseCammelCase,
lowerCaseCammelCase;
- private final DataSchemaNode node;
+ protected final DataSchemaNode node;
private static String getLocalName(DataSchemaNode attrNode) {
return attrNode.getQName().getLocalName();
*/
package org.opendaylight.controller.config.yangjmxgenerator.attribute;
-import javax.management.ObjectName;
-import javax.management.openmbean.OpenType;
-import javax.management.openmbean.SimpleType;
-
import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
import org.opendaylight.yangtools.binding.generator.util.Types;
import org.opendaylight.yangtools.sal.binding.model.api.Type;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import javax.management.ObjectName;
+import javax.management.openmbean.SimpleType;
+
public class DependencyAttribute extends AbstractAttribute implements
TypedAttribute {
}
@Override
- public OpenType<?> getOpenType() {
+ public SimpleType<?> getOpenType() {
return SimpleType.OBJECTNAME;
}
import org.opendaylight.yangtools.sal.binding.model.api.Type;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
private final Type type;
private final String nullableDescription, nullableDefault;
+ private final TypeProviderWrapper typeProviderWrapper;
+ private final TypeDefinition<?> typeDefinition;
public JavaAttribute(LeafSchemaNode leaf,
TypeProviderWrapper typeProviderWrapper) {
super(leaf);
this.type = typeProviderWrapper.getType(leaf);
+ this.typeDefinition = leaf.getType();
+ this.typeProviderWrapper = typeProviderWrapper;
this.nullableDefault = leaf.getDefault();
this.nullableDescription = leaf.getDescription();
}
TypeProviderWrapper typeProviderWrapper) {
super(leaf);
this.type = typeProviderWrapper.getType(leaf);
+ this.typeDefinition = leaf.getType();
+ this.typeProviderWrapper = typeProviderWrapper;
this.nullableDefault = null;
this.nullableDescription = leaf.getDescription();
}
+ /**
+ * Returns the most base type
+ */
+ private TypeDefinition<?> getBaseType(TypeProviderWrapper typeProviderWrapper, TypeDefinition<?> baseType) {
+ while(baseType.getBaseType()!=null) {
+ baseType = baseType.getBaseType();
+ }
+ return baseType;
+ }
+
@Override
public Type getType() {
return type;
@Override
public OpenType<?> getOpenType() {
- // If is array => arrayType
- if (isArray(getType())) {
- String innerTypeFullyQName = getInnerType(getType());
- SimpleType<?> innerSimpleType = SimpleTypeResolver
- .getSimpleType(innerTypeFullyQName);
- try {
- ArrayType<Object> arrayType = isPrimitive(innerTypeFullyQName) ? new ArrayType<>(
- innerSimpleType, true) : new ArrayType<>(1,
- innerSimpleType);
- return arrayType;
- } catch (OpenDataException e) {
- throw new RuntimeException("Unable to create "
- + ArrayType.class + " with inner element of type "
- + innerSimpleType, e);
- }
+ TypeDefinition<?> baseTypeDefinition = getBaseType(typeProviderWrapper, typeDefinition);
+ Type baseType = typeProviderWrapper.getType(baseTypeDefinition, baseTypeDefinition);
+
+ if (isArray()) {
+ return getArrayType();
+ } else if (isEnum(baseType)) {
+ return getSimpleType(baseType);
+ } else if (isDerivedType(baseType)) {
+ return getCompositeType(baseType, baseTypeDefinition);
}
- // else simple type
- SimpleType<?> simpleType = SimpleTypeResolver.getSimpleType(getType());
+
+ return getSimpleType(getType());
+ }
+
+ private boolean isEnum(Type baseType) {
+ return baseType.getFullyQualifiedName().equals(Enum.class.getName());
+ }
+
+ private OpenType<?> getSimpleType(Type type) {
+ SimpleType<?> simpleType = SimpleTypeResolver.getSimpleType(type);
return simpleType;
}
+ private OpenType<?> getCompositeType(Type baseType, TypeDefinition<?> baseTypeDefinition) {
+
+ SimpleType<?> innerItemType = SimpleTypeResolver.getSimpleType(baseType);
+ String innerItemName = typeProviderWrapper.getJMXParamForBaseType(baseTypeDefinition);
+
+ String[] itemNames = new String[]{innerItemName};
+ String description = getNullableDescription() == null ? getAttributeYangName() : getNullableDescription();
+
+ OpenType<?>[] itemTypes = new OpenType[]{innerItemType};
+ try {
+ return new CompositeType(getUpperCaseCammelCase(), description, itemNames, itemNames, itemTypes);
+ } catch (OpenDataException e) {
+ throw new RuntimeException("Unable to create " + CompositeType.class + " with inner element of type "
+ + itemTypes, e);
+ }
+
+ }
+
+ private OpenType<?> getArrayType() {
+ String innerTypeFullyQName = getInnerType(getType());
+ SimpleType<?> innerSimpleType = SimpleTypeResolver.getSimpleType(innerTypeFullyQName);
+ try {
+ ArrayType<Object> arrayType = isPrimitive(innerTypeFullyQName) ? new ArrayType<>(innerSimpleType, true)
+ : new ArrayType<>(1, innerSimpleType);
+ return arrayType;
+ } catch (OpenDataException e) {
+ throw new RuntimeException("Unable to create " + ArrayType.class + " with inner element of type "
+ + innerSimpleType, e);
+ }
+ }
+
// TODO verify
private boolean isPrimitive(String innerTypeFullyQName) {
if (innerTypeFullyQName.contains("."))
return true;
}
+ private boolean isArray() {
+ return type.getName().endsWith("[]");
+ }
+
+ private boolean isDerivedType(Type baseType) {
+ return baseType.equals(getType()) == false;
+ }
+
private static String getInnerType(Type type) {
String fullyQualifiedName = type.getFullyQualifiedName();
return fullyQualifiedName.substring(0, fullyQualifiedName.length() - 2);
}
- private static boolean isArray(Type type) {
- return type.getName().endsWith("[]");
- }
-
}
*/
package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+
+import javax.management.openmbean.SimpleType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.Map;
-import javax.management.openmbean.SimpleType;
-
-import org.opendaylight.yangtools.sal.binding.model.api.Type;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-
public class SimpleTypeResolver {
public static SimpleType<?> getSimpleType(Type type) {
return expectedSimpleType;
}
+ public static boolean canResolve(Type type) {
+ return JAVA_TYPE_TO_SIMPLE_TYPE.containsKey(type.getFullyQualifiedName());
+ }
+
public static SimpleType<?> getSimpleType(String fullyQualifiedName) {
SimpleType<?> expectedSimpleType = JAVA_TYPE_TO_SIMPLE_TYPE
.get(fullyQualifiedName);
return expectedSimpleType;
}
- private static final Map<String, SimpleType<?>> JAVA_TYPE_TO_SIMPLE_TYPE = Maps
- .newHashMap();
+ private static final Map<String, SimpleType<?>> JAVA_TYPE_TO_SIMPLE_TYPE = Maps.newHashMap();
static {
// TODO add all
- JAVA_TYPE_TO_SIMPLE_TYPE.put(Integer.class.getName(),
- SimpleType.INTEGER);
+ JAVA_TYPE_TO_SIMPLE_TYPE.put(Integer.class.getName(), SimpleType.INTEGER);
JAVA_TYPE_TO_SIMPLE_TYPE.put(int.class.getName(), SimpleType.INTEGER);
JAVA_TYPE_TO_SIMPLE_TYPE.put(Short.class.getName(), SimpleType.SHORT);
JAVA_TYPE_TO_SIMPLE_TYPE.put(short.class.getName(), SimpleType.SHORT);
JAVA_TYPE_TO_SIMPLE_TYPE.put(Long.class.getName(), SimpleType.LONG);
JAVA_TYPE_TO_SIMPLE_TYPE.put(long.class.getName(), SimpleType.LONG);
JAVA_TYPE_TO_SIMPLE_TYPE.put(String.class.getName(), SimpleType.STRING);
- JAVA_TYPE_TO_SIMPLE_TYPE.put(Boolean.class.getName(),
- SimpleType.BOOLEAN);
- JAVA_TYPE_TO_SIMPLE_TYPE.put(boolean.class.getName(),
- SimpleType.BOOLEAN);
- JAVA_TYPE_TO_SIMPLE_TYPE.put(BigInteger.class.getName(),
- SimpleType.BIGINTEGER);
- JAVA_TYPE_TO_SIMPLE_TYPE.put(BigDecimal.class.getName(),
- SimpleType.BIGDECIMAL);
+ JAVA_TYPE_TO_SIMPLE_TYPE.put(Enum.class.getName(), SimpleType.STRING);
+ JAVA_TYPE_TO_SIMPLE_TYPE.put(Boolean.class.getName(), SimpleType.BOOLEAN);
+ JAVA_TYPE_TO_SIMPLE_TYPE.put(boolean.class.getName(), SimpleType.BOOLEAN);
+ JAVA_TYPE_TO_SIMPLE_TYPE.put(BigInteger.class.getName(), SimpleType.BIGINTEGER);
+ JAVA_TYPE_TO_SIMPLE_TYPE.put(BigDecimal.class.getName(), SimpleType.BIGDECIMAL);
JAVA_TYPE_TO_SIMPLE_TYPE.put(Byte.class.getName(), SimpleType.BYTE);
JAVA_TYPE_TO_SIMPLE_TYPE.put(byte.class.getName(), SimpleType.BYTE);
JAVA_TYPE_TO_SIMPLE_TYPE.put(Date.class.getName(), SimpleType.DATE);
*/
package org.opendaylight.controller.config.yangjmxgenerator.attribute;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import javax.management.openmbean.CompositeType;
-import javax.management.openmbean.OpenDataException;
-import javax.management.openmbean.OpenType;
-
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import com.google.common.base.Function;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
public class TOAttribute extends AbstractAttribute {
createInnerAttribute(dataSchemaNode,
typeProviderWrapper));
} catch (IllegalArgumentException e) {
- throw new IllegalStateException("Unable to create TO");
+ throw new IllegalStateException("Unable to create TO", e);
}
}
return new TOAttribute(containerSchemaNode, map, attributeNameMap,
}
@Override
- public OpenType<?> getOpenType() {
+ public CompositeType getOpenType() {
String description = getNullableDescription() == null ? getAttributeYangName()
: getNullableDescription();
final String[] itemNames = new String[yangNameToAttributeMap.keySet()
</dependency>
</dependencies>
-
-
<build>
<plugins>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
+ <version>${opendaylight.yang.version}</version>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ </codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>
+ urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang
+ </namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+
+ <execution>
+ <id>types</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>src/main/yang/types</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ target/generated-sources/sal
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>${yangtools.binding.version}</version>
+ </dependency>
+ </dependencies>
</plugin>
</plugins>
</build>
package org.opendaylight.controller.config.yang.test.impl;
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
/**
*
*/
@Override
public java.lang.AutoCloseable createInstance() {
- return new AutoCloseable() {
+ return registerRuntimeBeans();
+ }
+
+ private NetconfTestImplRuntimeRegistration registerRuntimeBeans() {
+ NetconfTestImplRuntimeRegistration reg = getRootRuntimeBeanRegistratorWrapper().register(new NetconfTestImplRuntimeMXBean() {
+
+ @Override
+ public Long getCreatedSessions() {
+ return getSimpleLong();
+ }
+
@Override
- public void close() throws Exception {
+ public Asdf getAsdf() {
+ final Asdf asdf = new Asdf();
+ asdf.setSimpleString("asdf");
+ return asdf;
}
- };
+
+ @Override
+ public String noArg(final String arg1) {
+ return arg1.toUpperCase();
+ }
+
+ });
+
+ for (int i = 0; i < getSimpleShort(); i++) {
+ final int finalI = i;
+
+ reg.register(new InnerRunningDataAdditionalRuntimeMXBean() {
+ @Override
+ public Integer getSimpleInt3() {
+ return getSimpleTest();
+ }
+
+ @Override
+ public Deep4 getDeep4() {
+ final Deep4 d = new Deep4();
+ d.setBoool(false);
+ return d;
+ }
+
+ @Override
+ public String getSimpleString() {
+ return Integer.toString(finalI);
+ }
+
+ @Override
+ public void noArgInner() {
+ }
+ });
+
+ InnerRunningDataRuntimeRegistration innerReg = reg.register(new InnerRunningDataRuntimeMXBean() {
+ @Override
+ public Integer getSimpleInt3() {
+ return finalI;
+ }
+
+ @Override
+ public Deep2 getDeep2() {
+ return new Deep2();
+ }
+ });
+
+ for (int j = 0; j < getSimpleShort(); j++) {
+ final int finalJ = j;
+ innerReg.register(new InnerInnerRunningDataRuntimeMXBean() {
+ @Override
+ public List<NotStateBean> getNotStateBean() {
+ NotStateBean b1 = new NotStateBean();
+ b1.setElement("not state");
+ return Lists.newArrayList(b1);
+ }
+
+ @Override
+ public Integer getSimpleInt3() {
+ return finalJ;
+ }
+
+ @Override
+ public Deep3 getDeep3() {
+ return new Deep3();
+ }
+
+ @Override
+ public List<String> getListOfStrings() {
+ return Lists.newArrayList("l1", "l2");
+ }
+
+ @Override
+ public List<RetValList> listOutput() {
+ return Lists.newArrayList(new RetValList());
+ }
+
+ @Override
+ public Boolean noArgInnerInner(Integer integer, Boolean aBoolean) {
+ return aBoolean;
+ }
+
+ @Override
+ public RetValContainer containerOutput() {
+ return new RetValContainer();
+ }
+
+ @Override
+ public List<String> leafListOutput() {
+ return Lists.newArrayList("1", "2");
+ }
+ });
+ }
+ }
+
+ return reg;
}
}
import config { prefix config; revision-date 2013-04-05; }
import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
import rpc-context { prefix rpcx; revision-date 2013-06-17; }
-
+ import test-types { prefix tt; revision-date 2013-11-27; }
description
"Testing IMPL";
type string;
}
+ leaf extended {
+ type tt:extend-once;
+ }
+
+ leaf extended-twice {
+ type tt:extend-twice;
+ }
+
+ leaf extended-enum {
+ type tt:extend-enum;
+ }
+
container dto-c {
leaf simple-arg {
type uint32;
--- /dev/null
+module test-types {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:test:types";
+ prefix "tt";
+
+ description
+ "Types generated for testing";
+
+ revision "2013-11-27";
+
+ typedef extend-once {
+ type uint16;
+ }
+
+ typedef extend-twice {
+ type extend-once;
+ }
+
+ typedef extend-enum {
+ type enumeration {
+ enum "ONE";
+ enum "TWO";
+ }
+ }
+
+}
<artifactId>config-persister-file-adapter</artifactId>
<version>${config.version}</version>
</dependency>
-
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-directory-adapter</artifactId>
+ <version>${config.version}</version>
+ </dependency>
<!-- Netconf -->
<dependency>
<artifactId>netconf-mapping-api</artifactId>
<version>${netconf.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-ssh</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-netconf-connector</artifactId>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-model-api</artifactId>
</dependency>
-
<dependency>
<groupId>org.opendaylight.yangtools.model</groupId>
<artifactId>yang-ext</artifactId>
</dependency>
-
<dependency>
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>ganymed</artifactId>
netconf.tcp.address=0.0.0.0
netconf.tcp.port=8383
-#netconf.tls.address=127.0.0.1
-#netconf.tls.port=8384
-#netconf.tls.keystore=
-#netconf.tls.keystore.password=
-
-netconf.config.persister.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
-fileStorage=configuration/controller.config
-numberOfBackups=1
+netconf.ssh.address=0.0.0.0
+netconf.ssh.port=1830
+
+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
--- /dev/null
+//MODULES START
+//SERVICES START
+
+//CAPABILITIES START
+urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-//START OF CONFIG-LAST
-<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-<modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+//MODULES START
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
<name>yang-schema-service</name>
<name>ref_runtime-mapping-singleton</name>
</mapping-service>
</module>
-</modules>
-<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+//SERVICES START
<service>
<type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
<instance>
<provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
</instance>
</service>
-</services>
-</data>
-
-//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
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
<!-- Base log level -->
<logger name="org.opendaylight" level="INFO"/>
- <logger name="org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort" level="ERROR"/>
<!-- Controller log level -->
<logger name="org.opendaylight.controller" level="INFO"/>
public GroupConsumerImpl() {
- InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Groups.class).child(Group.class)
- .toInstance();
+ InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Groups.class).toInstance();
groupService = FRMConsumerImpl.getProviderSession().getRpcService(SalGroupService.class);
clusterGroupContainerService = FRMConsumerImpl.getClusterContainerService();
public MeterConsumerImpl() {
- InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Meters.class).child(Meter.class)
- .toInstance();
+ InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Meters.class).toInstance();
meterService = FRMConsumerImpl.getProviderSession().getRpcService(SalMeterService.class);
clusterMeterContainerService = FRMConsumerImpl.getClusterContainerService();
private boolean inContainerMode; // being used by global instance only
public TableFeaturesConsumerImpl() {
- InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Tables.class).child(Table.class)
- .toInstance();
+ InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Tables.class).toInstance();
tableService = FRMConsumerImpl.getProviderSession().getRpcService(SalTableService.class);
if (null == tableService) {
}
Map<InstanceIdentifier<?>, TableFeatures> updates = new HashMap<>();
+ Map<InstanceIdentifier<?>, TableFeatures> createdEntries = new HashMap<>();
/**
* We create a plan which table features will be updated.
*
*/
void prepareUpdate() {
+ Set<Entry<InstanceIdentifier<?>, DataObject>> createdEntries = modification.getCreatedConfigurationData().entrySet();
Set<Entry<InstanceIdentifier<?>, DataObject>> puts = modification.getUpdatedConfigurationData().entrySet();
for (Entry<InstanceIdentifier<?>, DataObject> entry : puts) {
enum flow-mod-failed;
enum group-mod-failed;
enum port-mod-failed;
- enum table-mod-failed;
+ enum table-mod-failed;
enum queue-op-failed;
enum switch-config-failed;
enum role-request-failed;
}
leaf code {
- type uint16;
+ type uint16;
}
leaf data {
}
leaf exp-type {
- type uint16;
+ type uint16;
}
leaf experimenter-id {
- type uint32;
+ type uint32;
}
leaf data {
type string;
}
- }
+ }
}
\ No newline at end of file
rpc add-flow {
input {
uses node-flow;
- uses tr:transaction-aware;
+ uses tr:transaction-aware;
}
output {
uses tr:transaction-aware;
rpc remove-flow {
input {
uses node-flow;
- uses tr:transaction-aware;
+ uses tr:transaction-aware;
}
output {
uses tr:transaction-aware;
rpc update-flow {
input {
uses flow-update;
- uses tr:transaction-aware;
+ uses tr:transaction-aware;
}
output {
uses tr:transaction-aware;
rpc add-group {
input {
uses node-group;
- uses tr:transaction-aware;
+ uses tr:transaction-aware;
}
output {
uses tr:transaction-aware;
rpc remove-group {
input {
uses node-group;
- uses tr:transaction-aware;
+ uses tr:transaction-aware;
}
output {
uses tr:transaction-aware;
rpc update-group {
input {
uses group-update;
- uses tr:transaction-aware;
+ uses tr:transaction-aware;
}
output {
uses tr:transaction-aware;
import flow-capable-transaction {prefix tr;}
contact
- "Anilkumar Vishnoi
- Email: avishnoi@in.ibm.com";
-
+ "Anilkumar Vishnoi
+ Email: avishnoi@in.ibm.com";
+
revision "2013-11-11" {
description "Initial revision of group statistics service";
}
augment "/inv:nodes/inv:node" {
ext:augment-identifier "node-group-statistics";
container group-statistics {
- //config "false";
- uses group-types:group-statistics-reply;
+ //config "false";
+ uses group-types:group-statistics-reply;
}
}
- augment "/inv:nodes/inv:node" {
+ augment "/inv:nodes/inv:node" {
ext:augment-identifier "node-group-desc-stats";
container group-desc {
- //config "false";
- uses group-types:group-desc-stats-reply;
+ //config "false";
+ uses group-types:group-desc-stats-reply;
}
}
-
- augment "/inv:nodes/inv:node" {
+
+ augment "/inv:nodes/inv:node" {
ext:augment-identifier "node-group-features";
container group-features {
- //config "false";
- uses group-types:group-features-reply;
+ //config "false";
+ uses group-types:group-features-reply;
}
}
- // RPC calls
- rpc get-all-group-statistics {
- input {
- uses inv:node;
+ // RPC calls
+ rpc get-all-group-statistics {
+ input {
+ uses inv:node-context-ref;
}
output {
- uses group-types:group-statistics-reply;
+ uses group-types:group-statistics-reply;
uses tr:transaction-aware;
}
-
- }
-
- rpc get-group-statistics {
- input {
- uses inv:node;
+
+ }
+
+ rpc get-group-statistics {
+ input {
+ uses inv:node-context-ref;
leaf group-id{
- type group-types:group-id;
+ type group-types:group-id;
}
}
uses group-types:group-statistics-reply;
uses tr:transaction-aware;
}
-
- }
-
- rpc get-group-description {
- input {
- uses inv:node;
+
+ }
+
+ rpc get-group-description {
+ input {
+ uses inv:node-context-ref;
}
output {
uses group-types:group-desc-stats-reply;
uses tr:transaction-aware;
}
- }
-
- rpc get-group-features {
- input {
- uses inv:node;
+ }
+
+ rpc get-group-features {
+ input {
+ uses inv:node-context-ref;
}
output {
uses group-types:group-features-reply;
uses tr:transaction-aware;
}
- }
-
+ }
+
- //Notification calls
-
- notification group-statistics-updated {
- leaf moreReplies {
- type boolean;
- }
- uses inv:node;
- uses group-types:group-statistics-reply;
+ //Notification calls
+
+ notification group-statistics-updated {
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses group-types:group-statistics-reply;
uses tr:transaction-aware;
- }
-
- notification group-desc-stats-updated {
- leaf moreReplies {
- type boolean;
- }
- uses inv:node;
- uses group-types:group-desc-stats-reply;
+ }
+
+ notification group-desc-stats-updated {
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses group-types:group-desc-stats-reply;
uses tr:transaction-aware;
- }
+ }
- notification group-features-updated {
- leaf moreReplies {
- type boolean;
- }
- uses inv:node;
- uses group-types:group-features-reply;
+ notification group-features-updated {
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses group-types:group-features-reply;
uses tr:transaction-aware;
- }
+ }
}
import flow-capable-transaction {prefix tr;}
contact
- "Anilkumar Vishnoi
- Email: avishnoi@in.ibm.com";
+ "Anilkumar Vishnoi
+ Email: avishnoi@in.ibm.com";
revision "2013-11-11" {
description "Initial revision of meter statistics service";
augment "/inv:nodes/inv:node" {
ext:augment-identifier "node-meter-statistics";
container meter-statistics {
- //config "false";
- uses meter-types:meter-statistics-reply;
+ //config "false";
+ uses meter-types:meter-statistics-reply;
}
}
- augment "/inv:nodes/inv:node" {
+ augment "/inv:nodes/inv:node" {
ext:augment-identifier "node-meter-config-stats";
container meter-config-stats {
- //config "false";
- uses meter-types:meter-config-stats-reply;
+ //config "false";
+ uses meter-types:meter-config-stats-reply;
}
}
-
- augment "/inv:nodes/inv:node" {
+
+ augment "/inv:nodes/inv:node" {
ext:augment-identifier "node-meter-features";
container meter-features {
- //config "false";
- uses meter-types:meter-features-reply;
+ //config "false";
+ uses meter-types:meter-features-reply;
}
}
- // RPC calls
- rpc get-all-meter-statistics {
- input {
- uses inv:node;
+ // RPC calls
+ rpc get-all-meter-statistics {
+ input {
+ uses inv:node-context-ref;
}
output {
- uses meter-types:meter-statistics-reply;
- uses tr:transaction-aware;
+ uses meter-types:meter-statistics-reply;
+ uses tr:transaction-aware;
}
-
- }
-
- rpc get-meter-statistics {
- input {
- uses inv:node;
+
+ }
+
+ rpc get-meter-statistics {
+ input {
+ uses inv:node-context-ref;
leaf meter-id{
- type meter-types:meter-id;
+ type meter-types:meter-id;
}
}
output {
uses meter-types:meter-statistics-reply;
uses tr:transaction-aware;
}
-
- }
-
- rpc get-all-meter-config-statistics {
- input {
- uses inv:node;
+
+ }
+
+ rpc get-all-meter-config-statistics {
+ input {
+ uses inv:node-context-ref;
}
output {
- uses meter-types:meter-config-stats-reply;
+ uses meter-types:meter-config-stats-reply;
uses tr:transaction-aware;
}
- }
-
- rpc get-meter-features {
- input {
- uses inv:node;
+ }
+
+ rpc get-meter-features {
+ input {
+ uses inv:node-context-ref;
}
output {
- uses meter-types:meter-features-reply;
+ uses meter-types:meter-features-reply;
uses tr:transaction-aware;
}
- }
-
+ }
+
- //Notification calls
-
- notification meter-statistics-updated {
- leaf moreReplies {
- type boolean;
- }
-
- uses inv:node;
+ //Notification calls
+
+ notification meter-statistics-updated {
+ leaf moreReplies {
+ type boolean;
+ }
+
+ uses inv:node;
uses meter-types:meter-statistics-reply;
uses tr:transaction-aware;
- }
-
- notification meter-config-stats-updated {
- leaf moreReplies {
- type boolean;
- }
-
+ }
+
+ notification meter-config-stats-updated {
+ leaf moreReplies {
+ type boolean;
+ }
+
uses inv:node;
- uses meter-types:meter-config-stats-reply;
- uses tr:transaction-aware;
- }
+ uses meter-types:meter-config-stats-reply;
+ uses tr:transaction-aware;
+ }
- notification meter-features-updated {
- leaf moreReplies {
- type boolean;
- }
-
- uses inv:node;
+ notification meter-features-updated {
+ leaf moreReplies {
+ type boolean;
+ }
+
+ uses inv:node;
uses meter-types:meter-features-reply;
uses tr:transaction-aware;
- }
+ }
}
<configuration>
<instructions>
<Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Import-Package>*,org.opendaylight.yangtools.yang.binding.annotations</Import-Package>
</instructions>
</configuration>
</plugin>
val targetCls = createClass(iface.directProxyName, supertype) [
field(DELEGATE_FIELD, iface);
implementMethodsFrom(supertype) [
- body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
+ body = '''
+ {
+ if(«DELEGATE_FIELD» == null) {
+ throw new java.lang.IllegalStateException("No provider is processing supplied message");
+ }
+ return ($r) «DELEGATE_FIELD».«it.name»($$);
+ }
+ '''
]
]
return targetCls.toClass(iface.classLoader).newInstance as T
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(), //
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() {
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.*") //
);
* 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.
*/
import javax.net.ssl.SSLContext;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.client.NetconfSshClientDispatcher;
+import org.opendaylight.controller.netconf.util.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.controller.netconf.util.handler.ssh.authentication.LoginPassword;
import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
import org.osgi.framework.BundleContext;
EventLoopGroup bossGroup = getBossThreadGroupDependency();
EventLoopGroup workerGroup = getWorkerThreadGroupDependency();
Optional<SSLContext> maybeContext = Optional.absent();
- NetconfClientDispatcher dispatcher = new NetconfClientDispatcher(maybeContext , bossGroup, workerGroup);
-
+ NetconfClientDispatcher dispatcher = null;
+ if(getTcpOnly()) {
+ dispatcher = new NetconfClientDispatcher(maybeContext , bossGroup, workerGroup);
+ } else {
+ AuthenticationHandler authHandler = new LoginPassword(getUsername(),getPassword());
+ dispatcher = new NetconfSshClientDispatcher(authHandler , bossGroup, workerGroup);
+ }
getDomRegistryDependency().registerProvider(device, bundleContext);
device.start(dispatcher);
leaf port {
type uint32;
}
+
+ leaf tcp-only {
+ type boolean;
+ }
+ leaf username {
+ type string;
+ }
+
+ leaf password {
+ type string;
+ }
container dom-registry {
uses config:service-ref {
refine type {
checkNotNull(parent);
checkNotNull(parentSchema);
+ List<String> 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<String> 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());
}
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) {
}
}
- private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
+ private List<String> resolveLongerPath(List<String> l1, List<String> l2) {
+ return l1.size() > l2.size() ? l1 : l2;
+ }
+
+ private SchemaLocation findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode,
+ Deque<String> pathIterator) {
+ Map<String, ChoiceNode> 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;
--- /dev/null
+package org.opendaylight.controller.sal.rest.impl;
+
+import java.util.*;
+
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class SchemaLocation {
+ final private List<String> location = new ArrayList<>();
+ final private DataSchemaNode schema;
+
+ public SchemaLocation(DataSchemaNode schema) {
+ this.schema = schema;
+ }
+
+ DataSchemaNode getSchema() {
+ return schema;
+ }
+
+ List<String> getLocation() {
+ return location;
+ }
+
+ SchemaLocation addPathPart(String partOfPath) {
+ location.add(partOfPath);
+ return this;
+ }
+
+}
static String convertCompositeNodeDataAndYangToJson(CompositeNode compositeNode, String yangPath,
String outputPath, String searchedModuleName, String searchedDataSchemaName) {
- String jsonResult = null;
- Set<Module> modules = null;
+ Set<Module> 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<Module> modules) {
+ assertNotNull("modules can't be null.", modules);
Module module = null;
if (searchedModuleName != null) {
for (Module m : modules) {
} 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<Module> resolveModules(String yangPath) {
+ Set<Module> 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()) {
} else if (module.getChildNodes().size() == 1) {
dataSchemaNode = module.getChildNodes().iterator().next();
}
+ return dataSchemaNode;
+ }
+
+ static String writeCompNodeWithSchemaContextToJson(CompositeNode compositeNode, String outputPath,
+ Set<Module> 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 {
--- /dev/null
+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<Module> 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);
+ }
+ }
+
+}
--- /dev/null
+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
--- /dev/null
+<cont>
+ <cont1c>
+ <lf11c>lf11c val</lf11c>
+ </cont1c>
+</cont>
\ No newline at end of file
--- /dev/null
+<cont>
+ <lflst1d>lflst1d_1 val</lflst1d>
+ <lflst1d>lflst1d_2 val</lflst1d>
+</cont>
\ No newline at end of file
--- /dev/null
+<cont>
+ <lst1b>
+ <lf11b>lf11b_1 val</lf11b>
+ </lst1b>
+ <lst1b>
+ <lf11b>lf11b_2 val</lf11b>
+ </lst1b>
+</cont>
\ No newline at end of file
--- /dev/null
+<cont>
+ <cont1c>
+ <lf11c>lf11c val</lf11c>
+ </cont1c>
+</cont>
\ No newline at end of file
--- /dev/null
+<cont>
+ <lf1>lf1 val</lf1>
+ <lf1a>121</lf1a>
+ <lf1ab>lf1ab val</lf1ab>
+</cont>
\ No newline at end of file
--- /dev/null
+<cont>
+ <lf1aa>lf1aa val</lf1aa>
+ <lf1>lf1 val</lf1>
+ <lf1a>121</lf1a>
+ <lf1aaa>lf1aaa val</lf1aaa>
+</cont>
\ No newline at end of file
--- /dev/null
+<cont>
+ <lf1aa>lf1aa val</lf1aa>
+ <lf1>lf1 val</lf1>
+ <lf1a>121</lf1a>
+ <lf1ab>lf1ab value</lf1ab>
+</cont>
\ No newline at end of file
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GetGroupFeaturesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.OpendaylightGroupStatisticsService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.GetAllMeterConfigStatisticsInputBuilder;
spLogger.debug("Statistics requester thread started with timer interval : {}",5000);
- statisticsRequesterThread.start();
+ //statisticsRequesterThread.start();
spLogger.info("Statistics Provider started.");
}
//We need to add check, so see if groups/meters are supported
//by the target node. Below check doesn't look good.
if(targetNode.getId().getValue().contains("openflow:")){
- sendAllGroupStatisticsRequest(targetNode);
+ InstanceIdentifier<Node> targetInstanceId = InstanceIdentifier.builder(Nodes.class).child(Node.class,targetNode.getKey()).toInstance();
+ NodeRef targetNodeRef = new NodeRef(targetInstanceId);
- sendAllMeterStatisticsRequest(targetNode);
+ sendAllGroupStatisticsRequest(targetNodeRef);
- sendGroupDescriptionRequest(targetNode);
+ sendAllMeterStatisticsRequest(targetNodeRef);
- sendGroupFeaturesRequest(targetNode);
+ sendGroupDescriptionRequest(targetNodeRef);
- sendMeterConfigStatisticsRequest(targetNode);
+ sendGroupFeaturesRequest(targetNodeRef);
- sendMeterFeaturesRequest(targetNode);
+ sendMeterConfigStatisticsRequest(targetNodeRef);
+
+ sendMeterFeaturesRequest(targetNodeRef);
}
}
}
- private void sendAllGroupStatisticsRequest(Node targetNode){
+ private void sendAllGroupStatisticsRequest(NodeRef targetNode){
final GetAllGroupStatisticsInputBuilder input = new GetAllGroupStatisticsInputBuilder();
- input.setId(targetNode.getId());
+ input.setNode(targetNode);
+ input.setNode(targetNode);
+ @SuppressWarnings("unused")
Future<RpcResult<GetAllGroupStatisticsOutput>> response =
groupStatsService.getAllGroupStatistics(input.build());
}
- private void sendGroupDescriptionRequest(Node targetNode){
+ private void sendGroupDescriptionRequest(NodeRef targetNode){
final GetGroupDescriptionInputBuilder input = new GetGroupDescriptionInputBuilder();
- input.setId(targetNode.getId());
-
+ input.setNode(targetNode);
+
+ @SuppressWarnings("unused")
Future<RpcResult<GetGroupDescriptionOutput>> response =
groupStatsService.getGroupDescription(input.build());
}
- private void sendGroupFeaturesRequest(Node targetNode){
+ private void sendGroupFeaturesRequest(NodeRef targetNode){
GetGroupFeaturesInputBuilder input = new GetGroupFeaturesInputBuilder();
- input.setId(targetNode.getId());
-
+ input.setNode(targetNode);
+
+ @SuppressWarnings("unused")
Future<RpcResult<GetGroupFeaturesOutput>> response =
groupStatsService.getGroupFeatures(input.build());
}
- private void sendAllMeterStatisticsRequest(Node targetNode){
+ private void sendAllMeterStatisticsRequest(NodeRef targetNode){
GetAllMeterStatisticsInputBuilder input = new GetAllMeterStatisticsInputBuilder();
- input.setId(targetNode.getId());
-
+ input.setNode(targetNode);
+
+ @SuppressWarnings("unused")
Future<RpcResult<GetAllMeterStatisticsOutput>> response =
meterStatsService.getAllMeterStatistics(input.build());
}
- private void sendMeterConfigStatisticsRequest(Node targetNode){
+ private void sendMeterConfigStatisticsRequest(NodeRef targetNode){
GetAllMeterConfigStatisticsInputBuilder input = new GetAllMeterConfigStatisticsInputBuilder();
- input.setId(targetNode.getId());
-
+ input.setNode(targetNode);
+
+ @SuppressWarnings("unused")
Future<RpcResult<GetAllMeterConfigStatisticsOutput>> response =
meterStatsService.getAllMeterConfigStatistics(input.build());
}
- private void sendMeterFeaturesRequest(Node targetNode){
+ private void sendMeterFeaturesRequest(NodeRef targetNode){
GetMeterFeaturesInputBuilder input = new GetMeterFeaturesInputBuilder();
- input.setId(targetNode.getId());
-
+ input.setNode(targetNode);
+
+ @SuppressWarnings("unused")
Future<RpcResult<GetMeterFeaturesOutput>> response =
meterStatsService.getMeterFeatures(input.build());
}
} catch (Throwable e) {
throw Exceptions.sneakyThrow(e);
}
-
}
}
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
public abstract class AttributeIfcSwitchStatement<T> {
+ protected AttributeIfc lastAttribute;
+
public T switchAttribute(AttributeIfc attributeIfc) {
+ this.lastAttribute = attributeIfc;
+
if (attributeIfc instanceof JavaAttribute) {
- return caseJavaAttribute((JavaAttribute) attributeIfc);
+ try {
+ return caseJavaAttribute(attributeIfc.getOpenType());
+ } catch (UnknownOpenTypeException e) {
+ throw getIllegalArgumentException(attributeIfc);
+ }
+
} else if (attributeIfc instanceof DependencyAttribute) {
- return caseDependencyAttribute((DependencyAttribute) attributeIfc);
+ return caseDependencyAttribute(((DependencyAttribute) attributeIfc).getOpenType());
} else if (attributeIfc instanceof ListAttribute) {
- return caseListAttribute((ListAttribute) attributeIfc);
+ return caseListAttribute(((ListAttribute) attributeIfc).getOpenType());
} else if (attributeIfc instanceof TOAttribute) {
- return caseTOAttribute((TOAttribute) attributeIfc);
+ return caseTOAttribute(((TOAttribute) attributeIfc).getOpenType());
+ }
+
+ throw getIllegalArgumentException(attributeIfc);
+ }
+
+ private IllegalArgumentException getIllegalArgumentException(AttributeIfc attributeIfc) {
+ return new IllegalArgumentException("Unknown attribute type " + attributeIfc.getClass() + ", " + attributeIfc
+ + " with open type:" + attributeIfc.getOpenType());
+ }
+
+ public final T caseJavaAttribute(OpenType<?> openType) {
+ if (openType instanceof SimpleType<?>) {
+ return caseJavaSimpleAttribute((SimpleType<?>) openType);
+ } else if (openType instanceof ArrayType<?>) {
+ return caseJavaArrayAttribute((ArrayType<?>) openType);
+ } else if (openType instanceof CompositeType) {
+ return caseJavaCompositeAttribute((CompositeType) openType);
}
- throw new IllegalArgumentException("Unknown attribute type " + attributeIfc.getClass() + ", " + attributeIfc);
+ throw new UnknownOpenTypeException("Unknown attribute open type " + openType);
}
- protected abstract T caseJavaAttribute(JavaAttribute attributeIfc);
+ protected abstract T caseJavaSimpleAttribute(SimpleType<?> openType);
+
+ protected abstract T caseJavaArrayAttribute(ArrayType<?> openType);
+
+ protected abstract T caseJavaCompositeAttribute(CompositeType openType);
- protected abstract T caseDependencyAttribute(DependencyAttribute attributeIfc);
+ protected abstract T caseDependencyAttribute(SimpleType<?> attributeIfc);
- protected abstract T caseTOAttribute(TOAttribute attributeIfc);
+ protected abstract T caseTOAttribute(CompositeType openType);
- protected abstract T caseListAttribute(ListAttribute attributeIfc);
+ protected abstract T caseListAttribute(ArrayType<?> openType);
+
+
+ private static class UnknownOpenTypeException extends RuntimeException {
+ public UnknownOpenTypeException(String message) {
+ super(message);
+ }
+ }
}
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
-import java.util.List;
-
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-public abstract class AbstractAttributeReadingStrategy<A extends AttributeIfc> implements AttributeReadingStrategy {
+import java.util.List;
+
+public abstract class AbstractAttributeReadingStrategy implements AttributeReadingStrategy {
- private final A attributeIfc;
+ private final String nullableDefault;
- public AbstractAttributeReadingStrategy(A attributeIfc) {
- this.attributeIfc = attributeIfc;
+ public AbstractAttributeReadingStrategy(String nullableDefault) {
+ this.nullableDefault = nullableDefault;
}
- public A getAttributeIfc() {
- return attributeIfc;
+ public String getNullableDefault() {
+ return nullableDefault;
}
@Override
public AttributeConfigElement readElement(List<XmlElement> configNodes) {
if (configNodes.size() == 0)
- return AttributeConfigElement.createNullValue(attributeIfc);
+ return AttributeConfigElement.createNullValue(nullableDefault);
return readElementHook(configNodes);
}
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
import com.google.common.collect.Lists;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import java.util.List;
-public class ArrayAttributeReadingStrategy extends AbstractAttributeReadingStrategy<AttributeIfc> {
+public class ArrayAttributeReadingStrategy extends AbstractAttributeReadingStrategy {
private final AttributeReadingStrategy innerStrategy;
* @param attributeIfc
* @param innerStrategy
*/
- public ArrayAttributeReadingStrategy(AttributeIfc attributeIfc, AttributeReadingStrategy innerStrategy) {
- super(attributeIfc);
+ public ArrayAttributeReadingStrategy(String nullableDefault, AttributeReadingStrategy innerStrategy) {
+ super(nullableDefault);
this.innerStrategy = innerStrategy;
}
for (int i = 0; i < configNodes.size(); i++) {
innerList.add(innerStrategy.readElement(Lists.newArrayList(configNodes.get(i))).getValue());
}
- return AttributeConfigElement.create(getAttributeIfc(), innerList);
+ return AttributeConfigElement.create(getNullableDefault(), innerList);
}
}
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
-import javax.management.openmbean.OpenType;
-
+import com.google.common.base.Optional;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy;
-import com.google.common.base.Optional;
+import javax.management.openmbean.OpenType;
/**
* Parsed xml element containing configuration for one attribute of an instance
}
public static AttributeConfigElement create(AttributeIfc attributeIfc, Object value) {
- return new AttributeConfigElement(attributeIfc.getNullableDefault(), value);
+ String nullableDefault = attributeIfc.getNullableDefault();
+ return create(nullableDefault, value);
+ }
+
+ public static AttributeConfigElement create(String nullableDefault, Object value) {
+ return new AttributeConfigElement(nullableDefault, value);
}
public static AttributeConfigElement createNullValue(AttributeIfc attributeIfc) {
return new AttributeConfigElement(attributeIfc.getNullableDefault(), null);
}
+ public static AttributeConfigElement createNullValue(String nullableDefault) {
+ return new AttributeConfigElement(nullableDefault, null);
+ }
+
+
public Object getValue() {
return value;
}
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-public class CompositeAttributeReadingStrategy extends AbstractAttributeReadingStrategy<TOAttribute> {
+public class CompositeAttributeReadingStrategy extends AbstractAttributeReadingStrategy {
private final Map<String, AttributeReadingStrategy> innerStrategies;
- public CompositeAttributeReadingStrategy(TOAttribute attributeIfc,
+ public CompositeAttributeReadingStrategy(String nullableDefault,
Map<String, AttributeReadingStrategy> innerStrategies) {
- super(attributeIfc);
+ super(nullableDefault);
this.innerStrategies = innerStrategies;
}
Map<String, Object> innerMap = Maps.newHashMap();
- Map<String, AttributeIfc> inner = getAttributeIfc().getYangPropertiesToTypesMap();
-
List<XmlElement> recognisedChildren = Lists.newArrayList();
- for (Entry<String, AttributeIfc> innerAttrEntry : inner.entrySet()) {
+ for (Entry<String, AttributeReadingStrategy> innerAttrEntry : innerStrategies.entrySet()) {
List<XmlElement> childItem = complexElement.getChildElementsWithSameNamespace(innerAttrEntry.getKey());
recognisedChildren.addAll(childItem);
- AttributeConfigElement resolvedInner = innerStrategies.get(innerAttrEntry.getKey()).readElement(childItem);
+ AttributeConfigElement resolvedInner = innerAttrEntry.getValue().readElement(childItem);
innerMap.put(innerAttrEntry.getKey(), resolvedInner.getValue());
}
complexElement.checkUnrecognisedElements(recognisedChildren);
- return AttributeConfigElement.create(getAttributeIfc(), innerMap);
+ return AttributeConfigElement.create(getNullableDefault(), innerMap);
}
}
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import java.util.List;
import java.util.Map;
-public class ObjectNameAttributeReadingStrategy extends AbstractAttributeReadingStrategy<AttributeIfc> {
+public class ObjectNameAttributeReadingStrategy extends AbstractAttributeReadingStrategy {
private static final Object PREFIX_SEPARATOR = ":";
- public ObjectNameAttributeReadingStrategy(DependencyAttribute attributeIfc) {
- super(attributeIfc);
+ public ObjectNameAttributeReadingStrategy(String nullableDefault) {
+ super(nullableDefault);
}
@Override
+ " but was " + configNodes.size());
Preconditions.checkNotNull(firstChild, "Element %s should be present", firstChild);
- return AttributeConfigElement.create(getAttributeIfc(), resolve(firstChild));
+ return AttributeConfigElement.create(getNullableDefault(), resolve(firstChild));
}
private ObjectNameAttributeMappingStrategy.MappedDependency resolve(XmlElement firstChild) {
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
+import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
import javax.management.openmbean.SimpleType;
import java.util.Map;
import java.util.Map.Entry;
}
@Override
- protected AttributeReadingStrategy caseJavaAttribute(JavaAttribute attributeIfc) {
- if (attributeIfc.getOpenType() instanceof SimpleType<?>)
- return new SimpleAttributeReadingStrategy(attributeIfc);
- else if (attributeIfc.getOpenType() instanceof ArrayType<?>) {
- SimpleAttributeReadingStrategy innerStrategy = new SimpleAttributeReadingStrategy(
- ((ArrayType<?>) attributeIfc.getOpenType()).getElementOpenType());
- return new ArrayAttributeReadingStrategy(attributeIfc, innerStrategy);
- }
- throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
- + " or " + ArrayType.class);
+ public AttributeReadingStrategy caseJavaSimpleAttribute(SimpleType<?> openType) {
+ return new SimpleAttributeReadingStrategy(lastAttribute.getNullableDefault());
+ }
+
+ @Override
+ public AttributeReadingStrategy caseJavaArrayAttribute(ArrayType<?> openType) {
+ SimpleAttributeReadingStrategy innerStrategy = new SimpleAttributeReadingStrategy(lastAttribute.getNullableDefault());
+ return new ArrayAttributeReadingStrategy(lastAttribute.getNullableDefault(), innerStrategy);
}
@Override
- protected AttributeReadingStrategy caseDependencyAttribute(DependencyAttribute attributeIfc) {
- return new ObjectNameAttributeReadingStrategy(attributeIfc);
+ public AttributeReadingStrategy caseJavaCompositeAttribute(CompositeType openType) {
+ Preconditions.checkState(openType.keySet().size() == 1, "Unexpected number of elements for open type %s, should be 1", openType);
+ String mappingKey = openType.keySet().iterator().next();
+ return new SimpleCompositeAttributeReadingStrategy(lastAttribute.getNullableDefault(), mappingKey);
}
@Override
- protected AttributeReadingStrategy caseTOAttribute(TOAttribute attributeIfc) {
- Map<String, AttributeIfc> inner = attributeIfc.getYangPropertiesToTypesMap();
+ protected AttributeReadingStrategy caseDependencyAttribute(SimpleType<?> openType) {
+ return new ObjectNameAttributeReadingStrategy(lastAttribute.getNullableDefault());
+ }
+
+ @Override
+ protected AttributeReadingStrategy caseTOAttribute(CompositeType openType) {
+ Preconditions.checkState(lastAttribute instanceof TOAttribute);
+ Map<String, AttributeIfc> inner = ((TOAttribute)lastAttribute).getYangPropertiesToTypesMap();
+
Map<String, AttributeReadingStrategy> innerStrategies = Maps.newHashMap();
for (Entry<String, AttributeIfc> innerAttrEntry : inner.entrySet()) {
innerStrategies.put(innerAttrEntry.getKey(), innerStrat);
}
- return new CompositeAttributeReadingStrategy(attributeIfc, innerStrategies);
+ return new CompositeAttributeReadingStrategy(lastAttribute.getNullableDefault(), innerStrategies);
}
@Override
- protected AttributeReadingStrategy caseListAttribute(ListAttribute attributeIfc) {
- AttributeIfc innerAttr = attributeIfc.getInnerAttribute();
- AttributeReadingStrategy innerStrategy = prepareReadingStrategy(key, innerAttr);
- return new ArrayAttributeReadingStrategy(attributeIfc, innerStrategy);
+ protected AttributeReadingStrategy caseListAttribute(ArrayType<?> openType) {
+ Preconditions.checkState(lastAttribute instanceof ListAttribute);
+ AttributeReadingStrategy innerStrategy = prepareReadingStrategy(key, ((ListAttribute) lastAttribute).getInnerAttribute());
+ return new ArrayAttributeReadingStrategy(lastAttribute.getNullableDefault(), innerStrategy);
}
}
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import javax.management.openmbean.OpenType;
import java.util.List;
-public class SimpleAttributeReadingStrategy extends AbstractAttributeReadingStrategy<AttributeIfc> {
+public class SimpleAttributeReadingStrategy extends AbstractAttributeReadingStrategy {
- public SimpleAttributeReadingStrategy(AttributeIfc attributeIfc) {
- super(attributeIfc);
- }
-
- /**
- * @param elementOpenType
- */
- public SimpleAttributeReadingStrategy(OpenType<?> elementOpenType) {
- super(new AttributeIfcWrapper(elementOpenType));
+ public SimpleAttributeReadingStrategy(String nullableDefault) {
+ super(nullableDefault);
}
@Override
String textContent = xmlElement.getTextContent();
Preconditions.checkNotNull(textContent, "This element should contain text %s", xmlElement);
- return AttributeConfigElement.create(getAttributeIfc(), textContent);
+ return AttributeConfigElement.create(getNullableDefault(), postprocessParsedValue(textContent));
}
- /**
- * Wrapper for JavaAttribute inner element attributes (in case JavaAttribute
- * is array)
- */
- static class AttributeIfcWrapper implements AttributeIfc {
-
- private final OpenType<?> elementOpenType;
-
- public AttributeIfcWrapper(OpenType<?> elementOpenType) {
- this.elementOpenType = elementOpenType;
- }
-
- @Override
- public String getAttributeYangName() {
- return null;
- }
-
- @Override
- public String getNullableDescription() {
- return null;
- }
-
- @Override
- public String getNullableDefault() {
- return null;
- }
-
- @Override
- public String getUpperCaseCammelCase() {
- return null;
- }
-
- @Override
- public String getLowerCaseCammelCase() {
- return null;
- }
-
- @Override
- public OpenType<?> getOpenType() {
- return elementOpenType;
- }
-
+ protected Object postprocessParsedValue(String textContent) {
+ return textContent;
}
+
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
+
+import com.google.common.collect.Maps;
+
+import java.util.HashMap;
+
+public class SimpleCompositeAttributeReadingStrategy extends SimpleAttributeReadingStrategy {
+
+
+ private final String key;
+
+ public SimpleCompositeAttributeReadingStrategy(String nullableDefault, String key) {
+ super(nullableDefault);
+ this.key = key;
+ }
+
+ protected Object postprocessParsedValue(String textContent) {
+ HashMap<String,String> map = Maps.newHashMap();
+ map.put(key, textContent);
+ return map;
+ }
+
+}
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping;
+import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
}
public AttributeMappingStrategy<?, ? extends OpenType<?>> prepareStrategy(AttributeIfc attributeIfc) {
+
+ if(attributeIfc instanceof DependencyAttribute) {
+ serviceNameOfDepAttr = ((DependencyAttribute)attributeIfc).getDependency().getSie().getQName().getLocalName();
+ namespaceOfDepAttr = ((DependencyAttribute)attributeIfc).getDependency().getSie().getQName().getNamespace().toString();
+ }
+
return switchAttribute(attributeIfc);
}
}
@Override
- protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseJavaAttribute(JavaAttribute attributeIfc) {
-
- if (attributeIfc.getOpenType() instanceof SimpleType<?>)
- return new SimpleAttributeMappingStrategy((SimpleType<?>) attributeIfc.getOpenType());
- else if (attributeIfc.getOpenType() instanceof ArrayType<?>) {
- ArrayType<?> arrayType = (ArrayType<?>) attributeIfc.getOpenType();
- AttributeMappingStrategy<?, ? extends OpenType<?>> innerStrategy = new SimpleAttributeMappingStrategy(
- (SimpleType<?>) arrayType.getElementOpenType());
- return new ArrayAttributeMappingStrategy(arrayType, innerStrategy);
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseJavaSimpleAttribute(SimpleType<?> openType) {
+ return new SimpleAttributeMappingStrategy(openType);
+ }
+
+ @Override
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseJavaArrayAttribute(ArrayType<?> openType) {
+
+ AttributeMappingStrategy<?, ? extends OpenType<?>> innerStrategy = new SimpleAttributeMappingStrategy(
+ (SimpleType<?>) openType.getElementOpenType());
+ return new ArrayAttributeMappingStrategy(openType, innerStrategy);
+ }
+
+ @Override
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseJavaCompositeAttribute(CompositeType openType) {
+ Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies = Maps.newHashMap();
+
+ Map<String, String> attributeMapping = Maps.newHashMap();
+
+ for (String innerAttributeKey : openType.keySet()) {
+
+ innerStrategies.put(innerAttributeKey, caseJavaAttribute(openType.getType(innerAttributeKey)));
+ attributeMapping.put(innerAttributeKey, innerAttributeKey);
}
- throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
- + " or " + ArrayType.class);
+
+ return new CompositeAttributeMappingStrategy(openType, innerStrategies, attributeMapping);
}
+ private String serviceNameOfDepAttr;
+ private String namespaceOfDepAttr;
+
@Override
protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseDependencyAttribute(
- DependencyAttribute attributeIfc) {
- String serviceName = attributeIfc.getDependency().getSie().getQName().getLocalName();
- String namespace = attributeIfc.getDependency().getSie().getQName().getNamespace().toString();
- return new ObjectNameAttributeMappingStrategy((SimpleType<?>) attributeIfc.getOpenType(), dependencyTracker,
- serviceName, namespace);
+ SimpleType<?> openType) {
+ return new ObjectNameAttributeMappingStrategy(openType, dependencyTracker,
+ serviceNameOfDepAttr, namespaceOfDepAttr);
}
@Override
- protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseTOAttribute(TOAttribute attributeIfc) {
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseTOAttribute(CompositeType openType) {
Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies = Maps.newHashMap();
- for (Entry<String, AttributeIfc> innerAttrEntry : attributeIfc.getJmxPropertiesToTypesMap().entrySet()) {
+ Preconditions.checkState(lastAttribute instanceof TOAttribute);
+ TOAttribute lastTO = (TOAttribute) lastAttribute;
+
+ for (Entry<String, AttributeIfc> innerAttrEntry : ((TOAttribute)lastAttribute).getJmxPropertiesToTypesMap().entrySet()) {
innerStrategies.put(innerAttrEntry.getKey(), prepareStrategy(innerAttrEntry.getValue()));
}
- return new CompositeAttributeMappingStrategy((CompositeType) attributeIfc.getOpenType(), innerStrategies,
- createJmxToYangMapping(attributeIfc));
+ return new CompositeAttributeMappingStrategy(openType, innerStrategies,
+ createJmxToYangMapping(lastTO));
}
@Override
- protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseListAttribute(ListAttribute attributeIfc) {
- return new ArrayAttributeMappingStrategy(attributeIfc.getOpenType(),
- prepareStrategy(attributeIfc.getInnerAttribute()));
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseListAttribute(ArrayType<?> openType) {
+ Preconditions.checkState(lastAttribute instanceof ListAttribute);
+ return new ArrayAttributeMappingStrategy(openType,
+ prepareStrategy(((ListAttribute) lastAttribute).getInnerAttribute()));
}
}
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.management.openmbean.ArrayType;
-import javax.management.openmbean.CompositeType;
-import javax.management.openmbean.OpenType;
-import javax.management.openmbean.SimpleType;
-
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import java.util.Map;
+import java.util.Map.Entry;
public class ObjectResolver extends AttributeIfcSwitchStatement<AttributeResolvingStrategy<?, ? extends OpenType<?>>> {
private final Services serviceTracker;
- private OpenType<?> openType;
public ObjectResolver(Services serviceTracker) {
this.serviceTracker = serviceTracker;
for (Entry<String, AttributeIfc> attrEntry : configDefinition.entrySet()) {
strategies.put(attrEntry.getKey(),
- prepareStrategy(attrEntry.getValue(), attrEntry.getValue().getOpenType()));
+ prepareStrategy(attrEntry.getValue()));
}
return strategies;
}
- private AttributeResolvingStrategy<?, ? extends OpenType<?>> prepareStrategy(AttributeIfc attributeIfc,
- OpenType<?> openType) {
+ private AttributeResolvingStrategy<?, ? extends OpenType<?>> prepareStrategy(AttributeIfc attributeIfc) {
- this.openType = openType;
return switchAttribute(attributeIfc);
}
}
@Override
- protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseJavaAttribute(JavaAttribute attributeIfc) {
- if (attributeIfc.getOpenType() instanceof SimpleType<?>)
- return new SimpleAttributeResolvingStrategy((SimpleType<?>) openType);
- else if (attributeIfc.getOpenType() instanceof ArrayType<?>) {
- ArrayType<?> arrayType = (ArrayType<?>) openType;
- SimpleType<?> innerType = (SimpleType<?>) arrayType.getElementOpenType();
- AttributeResolvingStrategy<?, ? extends OpenType<?>> strat = new SimpleAttributeResolvingStrategy(innerType);
- return new ArrayAttributeResolvingStrategy(strat, arrayType);
+ protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseJavaSimpleAttribute(SimpleType<?> openType) {
+ return new SimpleAttributeResolvingStrategy(openType);
+ }
+
+ @Override
+ protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseJavaArrayAttribute(ArrayType<?> openType) {
+ SimpleType<?> innerType = (SimpleType<?>) openType.getElementOpenType();
+ AttributeResolvingStrategy<?, ? extends OpenType<?>> strat = new SimpleAttributeResolvingStrategy(innerType);
+ return new ArrayAttributeResolvingStrategy(strat, openType);
+ }
+
+ @Override
+ protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseJavaCompositeAttribute(CompositeType openType) {
+ Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerMap = Maps.newHashMap();
+
+ Map<String, String> yangToJmxMapping = Maps.newHashMap();
+ for (String innerAttributeKey : openType.keySet()) {
+ innerMap.put(innerAttributeKey, caseJavaAttribute(openType.getType(innerAttributeKey)));
+ yangToJmxMapping.put(innerAttributeKey, innerAttributeKey);
}
- throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
- + " or " + ArrayType.class);
+ return new CompositeAttributeResolvingStrategy(innerMap, openType, yangToJmxMapping);
}
@Override
protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseDependencyAttribute(
- DependencyAttribute attributeIfc) {
+ SimpleType<?> openType) {
return new ObjectNameAttributeResolvingStrategy(serviceTracker);
}
@Override
- protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseTOAttribute(TOAttribute attributeIfc) {
- CompositeType compositeType = (CompositeType) openType;
+ protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseTOAttribute(CompositeType openType) {
+ Preconditions.checkState(lastAttribute instanceof TOAttribute);
+ TOAttribute toAttribute = (TOAttribute) lastAttribute;
+
Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerMap = Maps.newHashMap();
- for (String innerName : compositeType.keySet()) {
- Preconditions.checkState(attributeIfc instanceof TOAttribute, "Unexpected state, " + attributeIfc
- + " should be instance of " + TOAttribute.class.getName());
- AttributeIfc innerAttributeIfc = attributeIfc.getJmxPropertiesToTypesMap().get(innerName);
+
+ for (String innerName : openType.keySet()) {
+
+ AttributeIfc innerAttributeIfc = toAttribute.getJmxPropertiesToTypesMap().get(innerName);
innerMap.put(innerAttributeIfc.getAttributeYangName(),
- prepareStrategy(innerAttributeIfc, compositeType.getType(innerName)));
+ prepareStrategy(innerAttributeIfc));
}
- return new CompositeAttributeResolvingStrategy(innerMap, compositeType, createYangToJmxMapping(attributeIfc));
+ return new CompositeAttributeResolvingStrategy(innerMap, openType, createYangToJmxMapping(toAttribute));
}
@Override
- protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseListAttribute(ListAttribute attributeIfc) {
- ArrayType<?> arrayType = (ArrayType<?>) openType;
- OpenType<?> innerType = arrayType.getElementOpenType();
- AttributeIfc inner = attributeIfc.getInnerAttribute();
- return new ArrayAttributeResolvingStrategy(prepareStrategy(inner, innerType), arrayType);
+ protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseListAttribute(ArrayType<?> openType) {
+ Preconditions.checkState(lastAttribute instanceof ListAttribute);
+ AttributeIfc innerAttribute = ((ListAttribute) lastAttribute).getInnerAttribute();
+ return new ArrayAttributeResolvingStrategy(prepareStrategy(innerAttribute), openType);
}
}
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
import org.w3c.dom.Document;
import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
import javax.management.openmbean.SimpleType;
import java.util.Map;
import java.util.Map.Entry;
}
@Override
- protected AttributeWritingStrategy caseJavaAttribute(JavaAttribute attributeIfc) {
+ protected AttributeWritingStrategy caseJavaSimpleAttribute(SimpleType<?> openType) {
+ return new SimpleAttributeWritingStrategy(document, key);
+ }
- if (attributeIfc.getOpenType() instanceof SimpleType<?>)
- return new SimpleAttributeWritingStrategy(document, key);
- else if (attributeIfc.getOpenType() instanceof ArrayType<?>) {
- AttributeWritingStrategy innerStrategy = new SimpleAttributeWritingStrategy(document, key);
- return new ArrayAttributeWritingStrategy(innerStrategy);
- }
- throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
- + " or " + ArrayType.class);
+ @Override
+ protected AttributeWritingStrategy caseJavaArrayAttribute(ArrayType<?> openType) {
+ AttributeWritingStrategy innerStrategy = new SimpleAttributeWritingStrategy(document, key);
+ return new ArrayAttributeWritingStrategy(innerStrategy);
}
@Override
- protected AttributeWritingStrategy caseDependencyAttribute(DependencyAttribute attributeIfc) {
+ protected AttributeWritingStrategy caseJavaCompositeAttribute(CompositeType openType) {
+ return new SimpleCompositeAttributeWritingStrategy(document, key);
+ }
+
+ @Override
+ protected AttributeWritingStrategy caseDependencyAttribute(SimpleType<?> openType) {
return new ObjectNameAttributeWritingStrategy(document, key);
}
@Override
- protected AttributeWritingStrategy caseTOAttribute(TOAttribute attributeIfc) {
+ protected AttributeWritingStrategy caseTOAttribute(CompositeType openType) {
+ Preconditions.checkState(lastAttribute instanceof TOAttribute);
+
Map<String, AttributeWritingStrategy> innerStrats = Maps.newHashMap();
String currentKey = key;
- for (Entry<String, AttributeIfc> innerAttrEntry : attributeIfc.getYangPropertiesToTypesMap().entrySet()) {
+ for (Entry<String, AttributeIfc> innerAttrEntry : ((TOAttribute) lastAttribute).getYangPropertiesToTypesMap().entrySet()) {
AttributeWritingStrategy innerStrategy = prepareWritingStrategy(innerAttrEntry.getKey(),
innerAttrEntry.getValue(), document);
}
@Override
- protected AttributeWritingStrategy caseListAttribute(ListAttribute attributeIfc) {
- AttributeIfc inner = attributeIfc.getInnerAttribute();
- AttributeWritingStrategy innerStrategy = prepareWritingStrategy(key, inner, document);
+ protected AttributeWritingStrategy caseListAttribute(ArrayType<?> openType) {
+ Preconditions.checkState(lastAttribute instanceof ListAttribute);
+ AttributeIfc innerAttribute = ((ListAttribute) lastAttribute).getInnerAttribute();
+
+ AttributeWritingStrategy innerStrategy = prepareWritingStrategy(key, innerAttribute, document);
return new ArrayAttributeWritingStrategy(innerStrategy);
}
@Override
public void writeElement(Element parentElement, String namespace, Object value) {
+ value = preprocess(value);
Util.checkType(value, String.class);
Element innerNode = XmlUtil.createTextElement(document, key, (String) value);
XmlUtil.addNamespaceAttr(innerNode, namespace);
parentElement.appendChild(innerNode);
}
+ protected Object preprocess(Object value) {
+ return value;
+ }
+
+
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.w3c.dom.Document;
+
+import java.util.Map;
+
+public class SimpleCompositeAttributeWritingStrategy extends SimpleAttributeWritingStrategy {
+
+ /**
+ * @param document
+ * @param key
+ */
+ public SimpleCompositeAttributeWritingStrategy(Document document, String key) {
+ super(document, key);
+ }
+
+ protected Object preprocess(Object value) {
+ Util.checkType(value, Map.class);
+ Preconditions.checkArgument(((Map)value).size() == 1, "Unexpected number of values in %s, expected 1", value);
+ return ((Map)value).values().iterator().next();
+ }
+
+}
this.moduleNamesToConfigs = Collections.unmodifiableMap(moduleNamesToConfigs);
}
- private Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
- Services serviceTracker) {
+ public static Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
+ Services serviceTracker, Map<String, Map<String, ModuleConfig>> configs) {
Multimap<String, ObjectName> moduleToInstances = mapInstancesToModules(instancesToMap);
Map<String, Map<String, Collection<ObjectName>>> retVal = Maps.newLinkedHashMap();
- for (String namespace : moduleConfigs.keySet()) {
+ for (String namespace : configs.keySet()) {
Map<String, Collection<ObjectName>> innerRetVal = Maps.newHashMap();
- for (Entry<String, ModuleConfig> mbeEntry : moduleConfigs.get(namespace).entrySet()) {
+ for (Entry<String, ModuleConfig> mbeEntry : configs.get(namespace).entrySet()) {
String moduleName = mbeEntry.getKey();
Collection<ObjectName> instances = moduleToInstances.get(moduleName);
+ // TODO, this code does not support same module names from different namespaces
+ // Namespace should be present in ObjectName
+
if (instances == null)
continue;
return retVal;
}
- private void addServices(Services serviceTracker, Collection<ObjectName> instances,
+ private static void addServices(Services serviceTracker, Collection<ObjectName> instances,
Multimap<String, String> providedServices) {
for (ObjectName instanceOn : instances) {
for (Entry<String, String> serviceName : providedServices.entries()) {
Services serviceTracker = new Services();
Map<String, Map<String, Collection<ObjectName>>> moduleToInstances = getMappedInstances(instancesToMap,
- serviceTracker);
+ serviceTracker, moduleConfigs);
Element root = dataElement;
if (maybeNamespace.isPresent()) {
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime;
import com.google.common.collect.Sets;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
throw new IllegalStateException("Root runtime bean not found among " + runtimeBeanOns);
}
- public Element toXml(String namespace, String instanceName, Collection<ObjectName> runtimeBeanOns, Document document) {
- Element moduleElement = document.createElement(XmlNetconfConstants.MODULE_KEY);
+ public Element toXml(String namespace, Collection<ObjectName> runtimeBeanOns,
+ Document document, ModuleConfig moduleConfig, ObjectName configBeanON, Services serviceTracker) {
- final String prefix = getPrefix(namespace);
- Element typeElement = XmlUtil.createPrefixedTextElement(document, XmlNetconfConstants.TYPE_KEY, prefix,
- moduleName);
- XmlUtil.addPrefixedNamespaceAttr(typeElement, prefix, namespace);
- moduleElement.appendChild(typeElement);
-
- Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, instanceName);
- moduleElement.appendChild(nameElement);
+ Element moduleElement = moduleConfig.toXml(configBeanON, serviceTracker, document, namespace);
ObjectName rootName = findRoot(runtimeBeanOns);
return moduleElement;
}
- private String getPrefix(String namespace) {
- return XmlNetconfConstants.PREFIX;
- }
-
}
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.management.ObjectName;
+import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class Runtime {
private final Map<String, Map<String, ModuleRuntime>> moduleRuntimes;
+ private final Map<String, Map<String, ModuleConfig>> moduleConfigs;
- public Runtime(Map<String, Map<String, ModuleRuntime>> moduleRuntimes) {
+ public Runtime(Map<String, Map<String, ModuleRuntime>> moduleRuntimes,
+ Map<String, Map<String, ModuleConfig>> moduleConfigs) {
this.moduleRuntimes = moduleRuntimes;
+ this.moduleConfigs = moduleConfigs;
}
private Map<String, Multimap<String, ObjectName>> mapInstancesToModules(Set<ObjectName> instancesToMap) {
Map<String, Multimap<String, ObjectName>> retVal = Maps.newHashMap();
+ // TODO map to namepsace, prevent module name conflicts
+ // this code does not support same module names from different namespaces
+ // Namespace should be present in ObjectName
+
for (ObjectName objectName : instancesToMap) {
String moduleName = ObjectNameUtil.getFactoryName(objectName);
return retVal;
}
- public Element toXml(Set<ObjectName> instancesToMap, Document document) {
+ public Element toXml(Set<ObjectName> instancesToMap, Set<ObjectName> configBeans, Document document) {
+ Services serviceTracker = new Services();
+
Element root = document.createElement(XmlNetconfConstants.DATA_KEY);
Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY);
XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
root.appendChild(modulesElement);
- Map<String, Multimap<String, ObjectName>> moduleToInstances = mapInstancesToModules(instancesToMap);
+ Map<String, Multimap<String, ObjectName>> moduleToRuntimeInstance = mapInstancesToModules(instancesToMap);
+ Map<String, Map<String, Collection<ObjectName>>> moduleToConfigInstance = Config.getMappedInstances(
+ configBeans, serviceTracker, moduleConfigs);
+
+ for (String localNamespace : moduleConfigs.keySet()) {
+
+ Map<String, Collection<ObjectName>> instanceToMbe = moduleToConfigInstance.get(localNamespace);
- for (String localNamespace : moduleRuntimes.keySet()) {
- for (String moduleName : moduleRuntimes.get(localNamespace).keySet()) {
- Multimap<String, ObjectName> instanceToRbe = moduleToInstances.get(moduleName);
+ for (String moduleName : moduleConfigs.get(localNamespace).keySet()) {
+ Multimap<String, ObjectName> instanceToRbe = moduleToRuntimeInstance.get(moduleName);
- if (instanceToRbe == null)
- continue;
+ for (ObjectName instanceON : instanceToMbe.get(moduleName)) {
+ String instanceName = ObjectNameUtil.getInstanceName(instanceON);
- for (String instanceName : instanceToRbe.keySet()) {
- ModuleRuntime moduleRuntime = moduleRuntimes.get(localNamespace).get(moduleName);
- Element innerXml = moduleRuntime.toXml(localNamespace, instanceName, instanceToRbe.get(instanceName), document);
- modulesElement.appendChild(innerXml);
+ Element runtimeXml;
+ ModuleConfig moduleConfig = moduleConfigs.get(localNamespace).get(moduleName);
+ if(instanceToRbe==null || instanceToRbe.containsKey(instanceName) == false) {
+ runtimeXml = moduleConfig.toXml(instanceON, serviceTracker, document, localNamespace);
+ } else {
+ ModuleRuntime moduleRuntime = moduleRuntimes.get(localNamespace).get(moduleName);
+ runtimeXml = moduleRuntime.toXml(localNamespace, instanceToRbe.get(instanceName), document,
+ moduleConfig, instanceON, serviceTracker);
+ }
+ modulesElement.appendChild(runtimeXml);
}
}
return root;
}
+ private ObjectName findInstance(Collection<ObjectName> objectNames, String instanceName) {
+ for (ObjectName objectName : objectNames) {
+ String name = ObjectNameUtil.getInstanceName(objectName);
+ if(name.equals(instanceName))
+ return objectName;
+ }
+
+ throw new UnsupportedOperationException("Unable to find config bean instance under name " + instanceName + " among " + objectNames);
+ }
+
}
} catch (IllegalStateException e) {
logger.warn("Error parsing xml", e);
final Map<String, String> errorInfo = new HashMap<>();
- errorInfo.put(ErrorTag.missing_attribute.name(), "Missing value for 'target' attribute");
+ errorInfo.put(ErrorTag.missing_attribute.name(), "Error parsing xml: " + e.getMessage());
throw new NetconfDocumentedException(e.getMessage(), ErrorType.rpc, ErrorTag.missing_attribute,
ErrorSeverity.error, errorInfo);
} catch (final IllegalArgumentException e) {
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleConfig;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.InstanceRuntime;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.ModuleRuntime;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.Runtime;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.slf4j.Logger;
throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application,
ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo);
}
+
final Set<ObjectName> runtimeBeans = configRegistryClient.lookupRuntimeBeans();
- final Map<String, Map<String, ModuleRuntime>> moduleMappings = createModuleRuntimes(configRegistryClient,
+
+ //Transaction provider required only for candidate datastore
+ final Set<ObjectName> configBeans = Datastore.getInstanceQueryStrategy(Datastore.running, null)
+ .queryInstances(configRegistryClient);
+
+ final Map<String, Map<String, ModuleRuntime>> moduleRuntimes = createModuleRuntimes(configRegistryClient,
yangStoreSnapshot.getModuleMXBeanEntryMap());
- final Runtime runtime = new Runtime(moduleMappings);
+ final Map<String, Map<String, ModuleConfig>> moduleConfigs = GetConfig.transform(configRegistryClient,
+ yangStoreSnapshot.getModuleMXBeanEntryMap());
+
+ final Runtime runtime = new Runtime(moduleRuntimes, moduleConfigs);
- final Element element = runtime.toXml(runtimeBeans, document);
+ final Element element = runtime.toXml(runtimeBeans, configBeans, document);
logger.info("{} operation successful", GET);
}
// TODO refactor ... duplicate code
- private Map<String, Map<String, ModuleConfig>> transform(final ConfigRegistryClient configRegistryClient,
+ public static Map<String, Map<String, ModuleConfig>> transform(final ConfigRegistryClient configRegistryClient,
Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
return Maps.transformEntries(mBeanEntries,
new Maps.EntryTransformer<String, Map<String, ModuleMXBeanEntry>, Map<String, ModuleConfig>>() {
import org.junit.matchers.JUnitMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.opendaylight.controller.config.api.ModuleIdentifier;
-import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
-import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
import org.opendaylight.controller.config.yang.store.impl.MbeParser;
-import org.opendaylight.controller.config.yang.test.impl.Asdf;
import org.opendaylight.controller.config.yang.test.impl.ComplexDtoBInner;
import org.opendaylight.controller.config.yang.test.impl.ComplexList;
import org.opendaylight.controller.config.yang.test.impl.Deep;
-import org.opendaylight.controller.config.yang.test.impl.Deep2;
-import org.opendaylight.controller.config.yang.test.impl.Deep3;
-import org.opendaylight.controller.config.yang.test.impl.Deep4;
import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.DtoAInner;
import org.opendaylight.controller.config.yang.test.impl.DtoAInnerInner;
import org.opendaylight.controller.config.yang.test.impl.DtoC;
import org.opendaylight.controller.config.yang.test.impl.DtoD;
-import org.opendaylight.controller.config.yang.test.impl.InnerInnerRunningDataRuntimeMXBean;
-import org.opendaylight.controller.config.yang.test.impl.InnerRunningDataAdditionalRuntimeMXBean;
-import org.opendaylight.controller.config.yang.test.impl.InnerRunningDataRuntimeMXBean;
-import org.opendaylight.controller.config.yang.test.impl.InnerRunningDataRuntimeRegistration;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMXBean;
-import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplRuntimeMXBean;
-import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplRuntimeRegistration;
-import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplRuntimeRegistrator;
-import org.opendaylight.controller.config.yang.test.impl.NotStateBean;
-import org.opendaylight.controller.config.yang.test.impl.NotStateBeanInternal;
import org.opendaylight.controller.config.yang.test.impl.Peers;
-import org.opendaylight.controller.config.yang.test.impl.RetValContainer;
-import org.opendaylight.controller.config.yang.test.impl.RetValList;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
edit("netconfMessages/editConfig.xml");
checkBinaryLeafEdited(getConfigCandidate());
-
// default-operation:none, should not affect binary leaf
edit("netconfMessages/editConfig_none.xml");
checkBinaryLeafEdited(getConfigCandidate());
checkBinaryLeafEdited(response);
checkTypeConfigAttribute(response);
+ checkTypedefs(response);
+ checkEnum(response);
edit("netconfMessages/editConfig_remove.xml");
buf.append(XmlElement.fromDomElement(e).getTextContent());
}
assertEquals("810", buf.toString());
+ }
+
+ private void checkTypedefs(final Element response) {
+ NodeList children = response.getElementsByTagName("extended");
+ assertEquals(1, children.getLength());
+ children = response.getElementsByTagName("extended-twice");
+ assertEquals(1, children.getLength());
+ }
+
+ private void checkEnum(final Element response) {
+ XmlElement modulesElement = XmlElement.fromDomElement(response).getOnlyChildElement("data")
+ .getOnlyChildElement("modules");
+
+ String enumName = "extended-enum";
+ String enumContent = "TWO";
+
+ for (XmlElement moduleElement : modulesElement.getChildElements("module")) {
+ String name = moduleElement.getOnlyChildElement("name").getTextContent();
+ if(name.equals("test1")) {
+ XmlElement enumAttr = moduleElement.getOnlyChildElement(enumName);
+ assertEquals(enumContent, enumAttr.getTextContent());
+
+ return;
+ }
+ }
+
+ fail("Enum attribute " + enumName + ":" + enumContent + " not present in " + XmlUtil.toString(response));
}
private void checkTypeConfigAttribute(Element response) {
@Test
public void testConfigNetconfRuntime() throws Exception {
- ModuleIdentifier id = new ModuleIdentifier(NetconfTestImplModuleFactory.NAME, "instance");
- RootRuntimeBeanRegistrator rootReg = new RootRuntimeBeanRegistratorImpl(internalJmxRegistrator, id);
- NetconfTestImplRuntimeRegistrator registrator = new NetconfTestImplRuntimeRegistrator(rootReg);
-
- NetconfTestImplRuntimeRegistration a = registerRoot(registrator);
- InnerRunningDataRuntimeRegistration reg = registerInner(a);
- registerInner2(reg);
-
- id = new ModuleIdentifier(NetconfTestImplModuleFactory.NAME, "instance2");
- rootReg = new RootRuntimeBeanRegistratorImpl(internalJmxRegistrator, id);
- registrator = new NetconfTestImplRuntimeRegistrator(rootReg);
-
- a = registerRoot(registrator);
- registerAdditional(a);
- registerAdditional(a);
- registerAdditional(a);
- registerAdditional(a);
- reg = registerInner(a);
- registerInner2(reg);
- reg = registerInner(a);
- registerInner2(reg);
- registerInner2(reg);
- reg = registerInner(a);
- registerInner2(reg);
- registerInner2(reg);
- registerInner2(reg);
- reg = registerInner(a);
- registerInner2(reg);
- registerInner2(reg);
- registerInner2(reg);
- registerInner2(reg);
+ createModule(INSTANCE_NAME);
- Element response = get();
+ edit("netconfMessages/editConfig.xml");
+ checkBinaryLeafEdited(getConfigCandidate());
- System.err.println(XmlUtil.toString(response));
+ // check after edit
+ commit();
+ Element response = get();
- assertEquals(2, getElementsSize(response, "module"));
+ assertEquals(2/*With runtime beans*/ + 2 /*Without runtime beans*/, getElementsSize(response, "module"));
+ // data from state
assertEquals(2, getElementsSize(response, "asdf"));
- assertEquals(5, getElementsSize(response, "inner-running-data"));
- assertEquals(5, getElementsSize(response, "deep2"));
- assertEquals(11, getElementsSize(response, "inner-inner-running-data"));
- assertEquals(11, getElementsSize(response, "deep3"));
- assertEquals(11 * 2, getElementsSize(response, "list-of-strings"));
- assertEquals(4, getElementsSize(response, "inner-running-data-additional"));
- assertEquals(4, getElementsSize(response, "deep4"));
+ // data from running config
+ assertEquals(2, getElementsSize(response, "simple-short"));
+
+ assertEquals(8, getElementsSize(response, "inner-running-data"));
+ assertEquals(8, getElementsSize(response, "deep2"));
+ assertEquals(8 * 4, getElementsSize(response, "inner-inner-running-data"));
+ assertEquals(8 * 4, getElementsSize(response, "deep3"));
+ assertEquals(8 * 4 * 2, getElementsSize(response, "list-of-strings"));
+ assertEquals(8, getElementsSize(response, "inner-running-data-additional"));
+ assertEquals(8, getElementsSize(response, "deep4"));
// TODO assert keys
RuntimeRpc netconf = new RuntimeRpc(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID);
return response.getElementsByTagName(elementName).getLength();
}
- private Object registerAdditional(final NetconfTestImplRuntimeRegistration a) {
- class InnerRunningDataAdditionalRuntimeMXBeanTest implements InnerRunningDataAdditionalRuntimeMXBean {
-
- private final int simpleInt;
- private final String simpleString;
-
- public InnerRunningDataAdditionalRuntimeMXBeanTest(final int simpleInt, final String simpleString) {
- this.simpleInt = simpleInt;
- this.simpleString = simpleString;
- }
-
- @Override
- public Integer getSimpleInt3() {
- return this.simpleInt;
- }
-
- @Override
- public Deep4 getDeep4() {
- final Deep4 d = new Deep4();
- d.setBoool(false);
- return d;
- }
-
- @Override
- public String getSimpleString() {
- return this.simpleString;
- }
-
- @Override
- public void noArgInner() {
- }
-
- }
-
- final int simpleInt = counter++;
- return a.register(new InnerRunningDataAdditionalRuntimeMXBeanTest(simpleInt, "randomString_" + simpleInt));
- }
-
- private void registerInner2(final InnerRunningDataRuntimeRegistration reg) {
- class InnerInnerRunningDataRuntimeMXBeanTest implements InnerInnerRunningDataRuntimeMXBean {
-
- private final int simpleInt;
-
- public InnerInnerRunningDataRuntimeMXBeanTest(final int simpleInt) {
- this.simpleInt = simpleInt;
- }
-
- @Override
- public List<NotStateBean> getNotStateBean() {
- final NotStateBean notStateBean = new NotStateBean();
- final NotStateBeanInternal notStateBeanInternal = new NotStateBeanInternal();
- notStateBean.setNotStateBeanInternal(Lists.newArrayList(notStateBeanInternal));
- return Lists.newArrayList(notStateBean);
- }
-
- @Override
- public Integer getSimpleInt3() {
- return this.simpleInt;
- }
-
- @Override
- public Deep3 getDeep3() {
- return new Deep3();
- }
-
- @Override
- public List<String> getListOfStrings() {
- return Lists.newArrayList("l1", "l2");
- }
-
- @Override
- public List<RetValList> listOutput() {
- return Lists.newArrayList(new RetValList());
- }
-
- @Override
- public Boolean noArgInnerInner(Integer integer, Boolean aBoolean) {
- return aBoolean;
- }
-
- @Override
- public RetValContainer containerOutput() {
- return new RetValContainer();
- }
-
- @Override
- public List<String> leafListOutput() {
- return Lists.newArrayList("1", "2");
- }
-
- }
-
- reg.register(new InnerInnerRunningDataRuntimeMXBeanTest(counter++));
-
- }
-
- private static int counter = 1000;
-
- private InnerRunningDataRuntimeRegistration registerInner(final NetconfTestImplRuntimeRegistration a) {
-
- class InnerRunningDataRuntimeMXBeanTest implements InnerRunningDataRuntimeMXBean {
-
- private final int simpleInt;
-
- public InnerRunningDataRuntimeMXBeanTest(final int simpleInt) {
- this.simpleInt = simpleInt;
- }
-
- @Override
- public Integer getSimpleInt3() {
- return this.simpleInt;
- }
-
- @Override
- public Deep2 getDeep2() {
- return new Deep2();
- }
-
- }
- return a.register(new InnerRunningDataRuntimeMXBeanTest(counter++));
- }
-
- private NetconfTestImplRuntimeRegistration registerRoot(final NetconfTestImplRuntimeRegistrator registrator) {
- final NetconfTestImplRuntimeRegistration a = registrator.register(new NetconfTestImplRuntimeMXBean() {
-
- @Override
- public Long getCreatedSessions() {
- return 11L;
- }
-
- @Override
- public Asdf getAsdf() {
- final Asdf asdf = new Asdf();
- asdf.setSimpleInt(55);
- asdf.setSimpleString("asdf");
- return asdf;
- }
-
- @Override
- public String noArg(final String arg1) {
- return arg1.toUpperCase();
- }
-
- });
- return a;
- }
-
private Element executeOp(final NetconfOperation op, final String filename) throws ParserConfigurationException,
SAXException, IOException, NetconfDocumentedException {
private List<InputStream> getYangs() throws FileNotFoundException {
List<String> paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang",
- "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang",
+ "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang", "/META-INF/yang/test-types.yang",
"/META-INF/yang/ietf-inet-types.yang");
final Collection<InputStream> yangDependencies = new ArrayList<>();
for (String path : paths) {
<version>${bgpcep.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
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,
--- /dev/null
+/*
+ * 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<String> 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<String> requiredCapabilities, missingNamespaces;
+
+ private StripCapabilitiesResult(SortedSet<String> requiredCapabilities, SortedSet<String> missingNamespaces) {
+ this.requiredCapabilities = Collections.unmodifiableSortedSet(requiredCapabilities);
+ this.missingNamespaces = Collections.unmodifiableSortedSet(missingNamespaces);
+ }
+ }
+
+
+ @VisibleForTesting
+ static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set<String> allCapabilitiesFromHello,
+ Pattern ignoredMissingCapabilityRegex) {
+ // collect all namespaces
+ Set<String> foundNamespacesInXML = getNamespaces(configElement);
+ logger.trace("All capabilities {}\nFound namespaces in XML {}", allCapabilitiesFromHello, foundNamespacesInXML);
+ // required are referenced both in xml and hello
+ SortedSet<String> requiredCapabilities = new TreeSet<>();
+ // can be removed
+ Set<String> obsoleteCapabilities = new HashSet<>();
+ // are in xml but not in hello
+ SortedSet<String> 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<String> 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<String> getNamespaces(XmlElement element){
+ Set<String> result = new HashSet<>();
+ for (Entry<String,Attr> 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<String> getCapabilities() {
+ return stripCapabilitiesResult.requiredCapabilities;
+ }
+
+ @VisibleForTesting
+ Set<String> getMissingNamespaces(){
+ return stripCapabilitiesResult.missingNamespaces;
+ }
+
+ @Override
+ public String getConfigSnapshot() {
+ return configSnapshot;
+ }
+}
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;
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
public class ConfigPersisterNotificationHandler implements NotificationListener, Closeable {
private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
+ private static final int NETCONF_SEND_ATTEMPT_MS_DELAY = 1000;
+ private static final int NETCONF_SEND_ATTEMPTS = 20;
private final InetSocketAddress address;
private final EventLoopGroup nettyThreadgroup;
private 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<Persister.ConfigSnapshotHolder> maybeConfig = loadLastConfig();
+ Optional<ConfigSnapshotHolder> maybeConfig = loadLastConfig();
if (maybeConfig.isPresent()) {
logger.debug("Last config found {}", persister);
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) {
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<String> 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<Persister.ConfigSnapshotHolder> loadLastConfig() {
- Optional<Persister.ConfigSnapshotHolder> maybeConfigElement;
+ private Optional<ConfigSnapshotHolder> loadLastConfig() {
+ Optional<ConfigSnapshotHolder> maybeConfigElement;
try {
maybeConfigElement = persister.loadLastConfig();
} catch (IOException e) {
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));
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));
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) {
}
}
- 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);
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
}
@Override
- public void close() throws IOException {
+ public void close() {
logger.debug("close called");
}
}
--- /dev/null
+/*
+ * 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:<pre>
+ 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
+
+ </pre>
+ * 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<PersisterWithConfiguration> persisterWithConfigurations;
+
+ public PersisterAggregator(List<PersisterWithConfiguration> persisterWithConfigurations) {
+ this.persisterWithConfigurations = persisterWithConfigurations;
+
+ }
+
+ public static PersisterAggregator createFromProperties(PropertiesProviderBaseImpl propertiesProvider) {
+ List<PersisterWithConfiguration> 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<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ // iterate in reverse order
+ ListIterator<PersisterWithConfiguration> li = persisterWithConfigurations.listIterator(persisterWithConfigurations.size());
+ while(li.hasPrevious()) {
+ PersisterWithConfiguration persisterWithConfiguration = li.previous();
+ Optional<ConfigSnapshotHolder> configSnapshotHolderOptional = persisterWithConfiguration.storage.loadLastConfig();
+ if (configSnapshotHolderOptional.isPresent()) {
+ return configSnapshotHolderOptional;
+ }
+ }
+ // no storage had an answer
+ return Optional.absent();
+ }
+
+ @VisibleForTesting
+ List<PersisterWithConfiguration> 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 +
+ '}';
+ }
+}
+++ /dev/null
-/*
- * 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<PersisterImpl> 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<ConfigSnapshotHolder> 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 + "]";
- }
-}
--- /dev/null
+/**
+ * @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;
+ }
+}
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;
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<PersisterImpl> maybePersister = PersisterImpl.createFromProperties(context);
- if (maybePersister.isPresent() == false) {
- throw new IllegalStateException("No persister is defined in " + PersisterImpl.STORAGE_ADAPTER_CLASS_PROP
- + " property. For noop persister use " + NoOpStorageAdapter.class.getCanonicalName()
- + " . Persister is not operational");
- }
+ @Override
+ public void start(final BundleContext context) throws Exception {
+ logger.debug("ConfigPersister starting");
- Optional<TLSConfiguration> maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(context);
- Optional<InetSocketAddress> 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() {
--- /dev/null
+/**
+ * @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;
+ }
+}
--- /dev/null
+/*
+ * 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<String> allCapabilities = readLines("/capabilities-all.txt");
+ Set<String> 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<String> 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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<PersisterAggregator.PersisterWithConfiguration> 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<ConfigSnapshotHolder> 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<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ load++;
+ return Optional.absent();
+ }
+
+ static int props = 0;
+
+ @Override
+ public Persister instantiate(PropertiesProvider propertiesProvider) {
+ props++;
+ return this;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ }
+
+}
+++ /dev/null
-/*
- * 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<ConfigSnapshotHolder> 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
-
- }
-
- }
-
-}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+<configuration scan="true">
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <root level="error">
+ <appender-ref ref="STDOUT" />
+ </root>
+ <logger name="org.opendaylight.controller.netconf.persist.impl" level="TRACE"/>
+</configuration>
--- /dev/null
+<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
+ <name>hash-map-data-store</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
+ <name>dom-broker</name>
+ <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <name>ref_hash-map-data-store</name>
+ </data-store>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
+ <name>binding-broker-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <name>ref_binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
+ <name>binding-notification-broker</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
+ <name>binding-data-broker</name>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>ref_dom-broker</name>
+ </dom-broker>
+ <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>ref_runtime-mapping-singleton</name>
+ </mapping-service>
+ </module>
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <instance>
+ <name>ref_yang-schema-service</name>
+ <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <instance>
+ <name>ref_binding-notification-broker</name>
+ <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <instance>
+ <name>ref_hash-map-data-store</name>
+ <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+ <instance>
+ <name>ref_binding-broker-impl</name>
+ <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
+ <instance>
+ <name>ref_runtime-mapping-singleton</name>
+ <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <instance>
+ <name>ref_dom-broker</name>
+ <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <instance>
+ <name>ref_binding-data-broker</name>
+ <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
+ </instance>
+ </service>
+ </services>
+</data>
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
-
-import java.io.IOException;
-import java.util.Map;
-
import org.opendaylight.protocol.framework.AbstractProtocolSession;
import org.opendaylight.protocol.framework.ProtocolMessageDecoder;
import org.opendaylight.protocol.framework.ProtocolMessageEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
+import java.util.Map;
+
public abstract class NetconfSession extends AbstractProtocolSession<NetconfMessage> {
private ChannelHandler exiEncoder;
private String exiEncoderName;
private String removeAfterMessageSentname;
private String pmeName,pmdName;
- private final Channel channel;
+ protected final Channel channel;
private final SessionListener sessionListener;
private final long sessionId;
private boolean up = false;
private static final Logger logger = LoggerFactory.getLogger(NetconfSession.class);
- private static final int T = 0;
protected NetconfSession(SessionListener sessionListener, Channel channel, long sessionId) {
this.sessionListener = sessionListener;
javax.xml.xpath,
org.opendaylight.controller.netconf.api,
org.opendaylight.controller.netconf.util,
- org.opendaylight.controller.netconf.util.xml,
+ org.opendaylight.controller.netconf.util.*,
org.opendaylight.protocol.framework,
org.slf4j,
org.w3c.dom,
- org.xml.sax
+ org.xml.sax,
+ io.netty.handler.codec
</Import-Package>
</instructions>
</configuration>
private NetconfClient(String clientLabelForLogging, InetSocketAddress address, ReconnectStrategy strat, NetconfClientDispatcher netconfClientDispatcher) throws InterruptedException {
this.label = clientLabelForLogging;
dispatch = netconfClientDispatcher;
-
sessionListener = new NetconfClientSessionListener();
Future<NetconfClientSession> clientFuture = dispatch.createClient(address, sessionListener, strat);
this.address = address;
package org.opendaylight.controller.netconf.client;
import io.netty.channel.Channel;
-
-import java.util.Collection;
-
import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.protocol.framework.SessionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Collection;
+
public class NetconfClientSession extends NetconfSession {
private static final Logger logger = LoggerFactory.getLogger(NetconfClientSession.class);
public Collection<String> getServerCapabilities() {
return capabilities;
}
-
}
package org.opendaylight.controller.netconf.client;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import javax.net.ssl.SSLContext;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
+import org.opendaylight.controller.netconf.util.handler.FramingMechanismHandlerFactory;
+import org.opendaylight.controller.netconf.util.handler.NetconfMessageAggregator;
+import org.opendaylight.controller.netconf.util.handler.ssh.SshHandler;
+import org.opendaylight.controller.netconf.util.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.controller.netconf.util.handler.ssh.client.Invoker;
+import org.opendaylight.controller.netconf.util.messages.FramingMechanism;
+import org.opendaylight.controller.netconf.util.messages.NetconfMessageFactory;
+import org.opendaylight.protocol.framework.ProtocolHandlerFactory;
+import org.opendaylight.protocol.framework.ProtocolMessageDecoder;
+import org.opendaylight.protocol.framework.ProtocolMessageEncoder;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+
+import com.google.common.base.Optional;
+
+import io.netty.channel.ChannelHandler;
import io.netty.channel.EventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.Promise;
public class NetconfSshClientDispatcher extends NetconfClientDispatcher {
- public NetconfSshClientDispatcher(EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
- super(null, bossGroup, workerGroup);
+ private AuthenticationHandler authHandler;
+ private HashedWheelTimer timer;
+ private NetconfClientSessionNegotiatorFactory negotatorFactory;
+
+ public NetconfSshClientDispatcher(AuthenticationHandler authHandler, EventLoopGroup bossGroup,
+ EventLoopGroup workerGroup) {
+ super(Optional.<SSLContext> absent(), bossGroup, workerGroup);
+ this.authHandler = authHandler;
+ this.timer = new HashedWheelTimer();
+ this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer);
+ }
+
+ @Override
+ public Future<NetconfClientSession> createClient(InetSocketAddress address,
+ final NetconfClientSessionListener sessionListener, ReconnectStrategy strat) {
+ return super.createClient(address, strat, new PipelineInitializer<NetconfClientSession>() {
+
+ @Override
+ public void initializeChannel(SocketChannel arg0, Promise<NetconfClientSession> arg1) {
+ new NetconfSshClientInitializer(authHandler, negotatorFactory, sessionListener).initialize(arg0, arg1);
+ }
+
+ });
+ }
+
+ private static final class NetconfSshClientInitializer extends AbstractChannelInitializer {
+
+ private final NetconfHandlerFactory handlerFactory;
+ private final AuthenticationHandler authenticationHandler;
+ private final NetconfClientSessionNegotiatorFactory negotiatorFactory;
+ private final NetconfClientSessionListener sessionListener;
+
+ public NetconfSshClientInitializer(AuthenticationHandler authHandler,
+ NetconfClientSessionNegotiatorFactory negotiatorFactory,
+ final NetconfClientSessionListener sessionListener) {
+ this.handlerFactory = new NetconfHandlerFactory(new NetconfMessageFactory());
+ this.authenticationHandler = authHandler;
+ this.negotiatorFactory = negotiatorFactory;
+ this.sessionListener = sessionListener;
+ }
+
+ @Override
+ public void initialize(SocketChannel ch, Promise<? extends NetconfSession> promise) {
+ try {
+ Invoker invoker = Invoker.subsystem("netconf");
+ ch.pipeline().addFirst(new SshHandler(authenticationHandler, invoker));
+ ch.pipeline().addLast("aggregator", new NetconfMessageAggregator(FramingMechanism.EOM));
+ ch.pipeline().addLast(handlerFactory.getDecoders());
+ initializeAfterDecoder(ch, promise);
+ ch.pipeline().addLast("frameEncoder",
+ FramingMechanismHandlerFactory.createHandler(FramingMechanism.EOM));
+ ch.pipeline().addLast(handlerFactory.getEncoders());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void initializeAfterDecoder(SocketChannel ch, Promise<? extends NetconfSession> promise) {
+ ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(new SessionListenerFactory() {
+ @Override
+ public SessionListener<NetconfMessage, NetconfClientSession, NetconfTerminationReason> getSessionListener() {
+ return sessionListener;
+ }
+ }, ch, promise));
+
+ }
+ }
+
+ private static final class NetconfHandlerFactory extends ProtocolHandlerFactory<NetconfMessage> {
+
+ public NetconfHandlerFactory(final NetconfMessageFactory msgFactory) {
+ super(msgFactory);
+ }
+
+ @Override
+ public ChannelHandler[] getEncoders() {
+ return new ChannelHandler[] { new ProtocolMessageEncoder(this.msgFactory) };
+ }
+
+ @Override
+ public ChannelHandler[] getDecoders() {
+ return new ChannelHandler[] { new ProtocolMessageDecoder(this.msgFactory) };
+ }
}
}
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;
private static final Logger logger = LoggerFactory.getLogger(NetconfImplActivator.class);
- private Optional<InetSocketAddress> maybeTCPAddress;
- private Optional<TLSConfiguration> maybeTLSConfiguration;
-
private NetconfOperationServiceFactoryTracker factoriesTracker;
private DefaultCommitNotificationProducer commitNot;
private NetconfServerDispatcher dispatch;
@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();
eventLoopGroup = new NioEventLoopGroup();
- if (maybeTCPAddress.isPresent()) {
- Optional<SSLContext> maybeSSLContext = Optional.absent();
- InetSocketAddress address = maybeTCPAddress.get();
- NetconfServerDispatcher.ServerSslChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerSslChannelInitializer(
- maybeSSLContext, serverNegotiatorFactory, listenerFactory);
- dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
-
- logger.info("Starting TCP netconf server at {}", address);
- dispatch.createServer(address);
- }
- if (maybeTLSConfiguration.isPresent()) {
- Optional<SSLContext> maybeSSLContext = Optional.of(maybeTLSConfiguration.get().getSslContext());
- InetSocketAddress address = maybeTLSConfiguration.get().getAddress();
- NetconfServerDispatcher.ServerSslChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerSslChannelInitializer(
- maybeSSLContext, serverNegotiatorFactory, listenerFactory);
- dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
-
- logger.info("Starting TLS netconf server at {}", address);
- dispatch.createServer(address);
- }
+ NetconfServerDispatcher.ServerSslChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerSslChannelInitializer(
+ Optional.<SSLContext>absent(), serverNegotiatorFactory, listenerFactory);
+ dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
+
+ logger.info("Starting TCP netconf server at {}", address);
+ dispatch.createServer(address);
+
}
@Override
<artifactId>yang-store-api</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-test</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-api</artifactId>
<artifactId>config-netconf-connector</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>yang-test</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>config-manager</artifactId>
<artifactId>netconf-mapping-api</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-ssh</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-util</artifactId>
<goal>test</goal>
</goals>
<configuration>
- <includes>
- <include>**/org/opendaylight/controller/netconf/it/*.java</include>
- </includes>
<skip>false</skip>
+ <argLine>-Dlogback.configurationFile=${maven.test.dest}/logback.xml</argLine>
</configuration>
</execution>
</executions>
package org.opendaylight.controller.netconf.it;
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+import javax.management.ObjectName;
+import javax.net.ssl.SSLContext;
+import javax.xml.parsers.ParserConfigurationException;
+import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
-import org.opendaylight.controller.config.manager.impl.jmx.BaseJMXRegistrator;
-import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
import org.opendaylight.controller.config.yang.store.api.YangStoreException;
import org.opendaylight.controller.config.yang.store.impl.HardcodedYangStoreService;
-import org.opendaylight.controller.config.yang.test.impl.Asdf;
import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMXBean;
-import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplRuntimeMXBean;
-import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplRuntimeRegistrator;
import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.client.NetconfClient;
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;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
-
-import javax.management.ObjectName;
-import javax.net.ssl.SSLContext;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
+import static java.util.Collections.emptyList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.internal.util.Checks.checkNotNull;
public class NetconfITTest extends AbstractConfigTest {
- // private static final Logger logger =
- // LoggerFactory.getLogger(NetconfITTest.class);
+ private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
//
private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
+ private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830);
+ private static final String USERNAME = "netconf";
+ private static final String PASSWORD = "netconf";
private NetconfMessage getConfig, getConfigCandidate, editConfig,
closeSession, startExi, stopExi;
private NetconfClientDispatcher clientDispatcher;
+
@Before
public void setUp() throws Exception {
super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray(
static Collection<InputStream> getBasicYangs() throws IOException {
List<String> paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang",
- "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang",
+ "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang", "/META-INF/yang/test-types.yang",
"/META-INF/yang/ietf-inet-types.yang");
final Collection<InputStream> yangDependencies = new ArrayList<>();
+ List<String> failedToFind = new ArrayList<>();
for (String path : paths) {
- final InputStream is = checkNotNull(NetconfITTest.class.getResourceAsStream(path), path + " not found");
- yangDependencies.add(is);
+ InputStream resourceAsStream = NetconfITTest.class.getResourceAsStream(path);
+ if (resourceAsStream == null) {
+ failedToFind.add(path);
+ } else {
+ yangDependencies.add(resourceAsStream);
+ }
}
+ assertEquals("Some yang files were not found",emptyList(), failedToFind);
return yangDependencies;
}
@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)) {
}
}
}
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();
}
NetconfTestImplModuleMXBean proxy = configRegistryClient
.newMXBeanProxy(impl, NetconfTestImplModuleMXBean.class);
proxy.setTestingDep(dep);
- registerRuntimeBean();
+ proxy.setSimpleShort((short)0);
transaction.commit();
}
}
- private void registerRuntimeBean() {
- BaseJMXRegistrator baseJMXRegistrator = new BaseJMXRegistrator(ManagementFactory.getPlatformMBeanServer());
- RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator = baseJMXRegistrator
- .createRuntimeBeanRegistrator(new ModuleIdentifier(NetconfTestImplModuleFactory.NAME, "instance"));
- NetconfTestImplRuntimeRegistrator reg = new NetconfTestImplRuntimeRegistrator(runtimeBeanRegistrator);
- reg.register(new NetconfTestImplRuntimeMXBean() {
- @Override
- public Asdf getAsdf() {
- return null;
- }
-
- @Override
- public Long getCreatedSessions() {
- return null;
- }
-
- @Override
- public String noArg(String arg1) {
- return "from no arg";
- }
- });
- }
-
@Test
// @Ignore
public void testStartExi() throws Exception {
return netconfClient;
}
+ private void startSSHServer() throws Exception{
+ logger.info("Creating SSH server");
+ Thread sshServerThread = new Thread(NetconfSSHServer.start(10830,tcpAddress));
+ sshServerThread.setDaemon(true);
+ sshServerThread.start();
+ logger.info("SSH server on");
+ }
+
+ @Test
+ public void sshTest() throws Exception {
+ startSSHServer();
+ logger.info("creating connection");
+ Connection conn = new Connection(sshAddress.getHostName(),sshAddress.getPort());
+ Assert.assertNotNull(conn);
+ logger.info("connection created");
+ conn.connect();
+ boolean isAuthenticated = conn.authenticateWithPassword(USERNAME,PASSWORD);
+ assertTrue(isAuthenticated);
+ logger.info("user authenticated");
+ final Session sess = conn.openSession();
+ sess.startSubSystem("netconf");
+ logger.info("user authenticated");
+ sess.getStdin().write(XmlUtil.toString(this.getConfig.getDocument()).getBytes());
+
+ new Thread(){
+ public void run(){
+ while (true){
+ byte[] bytes = new byte[1024];
+ int c = 0;
+ try {
+ c = sess.getStdout().read(bytes);
+ } catch (IOException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ logger.info("got data:"+bytes);
+ if (c == 0) break;
+ }
+ }
+ }.join();
+ }
+
+
}
--- /dev/null
+<configuration scan="true">
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.opendaylight.controller.netconf" level="DEBUG"/>
+
+ <root level="error">
+ <appender-ref ref="STDOUT" />
+ </root>
+
+</configuration>
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.3-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>netconf-ssh</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>ganymed</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.netconf.osgi.NetconfSSHActivator</Bundle-Activator>
+ <Export-Package>
+ org.opendaylight.controller.netconf.ssh,
+ </Export-Package>
+ <Import-Package>
+ com.google.common.base,
+ com.google.common.collect,
+ ch.ethz.ssh2,
+ ch.ethz.ssh2.signature,
+ io.netty.buffer,
+ io.netty.channel,
+ io.netty.channel.nio,
+ io.netty.channel.socket,
+ io.netty.util,
+ io.netty.util.concurrent,
+ javax.annotation,
+ java.net,
+ javax.net.ssl,
+ javax.xml.namespace,
+ javax.xml.parsers,
+ javax.xml.xpath,
+ org.apache.commons.io,
+ org.opendaylight.controller.netconf.api,
+ org.opendaylight.controller.netconf.client,
+ org.opendaylight.controller.netconf.util,
+ org.opendaylight.controller.netconf.util.osgi,
+ org.opendaylight.controller.netconf.util.xml,
+ org.opendaylight.protocol.framework,
+ org.osgi.framework,
+ org.slf4j,
+ org.w3c.dom,
+ org.xml.sax
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.osgi;
+
+import com.google.common.base.Optional;
+import java.net.InetSocketAddress;
+import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator
+ * starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket
+ * and listen for client connections. Each client connection creation is handled in separate
+ * {@link org.opendaylight.controller.netconf.ssh.threads.SocketThread} thread.
+ * This thread creates two additional threads {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}
+ * forwarding data from/to client.IOThread closes servers session and server connection when it gets -1 on input stream.
+ * {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}'s run method waits for -1 on input stream to finish.
+ * All threads are daemons.
+ **/
+public class NetconfSSHActivator implements BundleActivator{
+
+ private NetconfSSHServer server;
+ private static final Logger logger = LoggerFactory.getLogger(NetconfSSHActivator.class);
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+
+ logger.trace("Starting netconf SSH bridge.");
+
+ Optional<InetSocketAddress> sshSocketAddressOptional = NetconfConfigUtil.extractSSHNetconfAddress(context);
+ InetSocketAddress tcpSocketAddress = NetconfConfigUtil.extractTCPNetconfAddress(context,
+ "TCP is not configured, netconf ssh bridge is not available.");
+
+ if (sshSocketAddressOptional.isPresent()){
+ server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress);
+ Thread serverThread = new Thread(server,"netconf SSH server thread");
+ serverThread.setDaemon(true);
+ serverThread.start();
+ logger.trace("Netconf SSH bridge up and running.");
+ } else {
+ logger.trace("No valid connection configuration for SSH bridge found.");
+ throw new Exception("No valid connection configuration for SSH bridge found.");
+ }
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ if (server != null){
+ logger.trace("Netconf SSH bridge going down ...");
+ server.stop();
+ logger.trace("Netconf SSH bridge is down ...");
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.ssh;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.controller.netconf.ssh.threads.SocketThread;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ThreadSafe
+public class NetconfSSHServer implements Runnable {
+
+ private static boolean acceptMore = true;
+ private ServerSocket ss = null;
+ private static final Logger logger = LoggerFactory.getLogger(NetconfSSHServer.class);
+ private static final AtomicLong sesssionId = new AtomicLong();
+ private final InetSocketAddress clientAddress;
+
+ private NetconfSSHServer(int serverPort,InetSocketAddress clientAddress) throws Exception{
+
+ logger.trace("Creating SSH server socket on port {}",serverPort);
+ this.ss = new ServerSocket(serverPort);
+ if (!ss.isBound()){
+ throw new Exception("Socket can't be bound to requested port :"+serverPort);
+ }
+ logger.trace("Server socket created.");
+ this.clientAddress = clientAddress;
+
+ }
+
+
+ public static NetconfSSHServer start(int serverPort, InetSocketAddress clientAddress) throws Exception {
+ return new NetconfSSHServer(serverPort, clientAddress);
+ }
+
+ public void stop() throws Exception {
+ acceptMore = false;
+ logger.trace("Closing SSH server socket.");
+ ss.close();
+ logger.trace("SSH server socket closed.");
+ }
+
+ @Override
+ public void run() {
+ while (acceptMore) {
+ logger.trace("Starting new socket thread.");
+ try {
+ SocketThread.start(ss.accept(), clientAddress, sesssionId.incrementAndGet());
+ } catch (IOException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.ssh.authentication;
+
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+
+public interface KeyStoreHandler {
+ public RSAPrivateKey getPrivateKey();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.ssh.authentication;
+
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+
+import java.math.BigInteger;
+
+public class RSAKey implements KeyStoreHandler {
+
+ private static RSAPrivateKey hostkey = null;
+ private static String user = "netconf";
+ private static String password = "netconf";
+ static {
+
+ BigInteger p = new BigInteger("2967886344240998436887630478678331145236162666668503940430852241825039192450179076148979094256007292741704260675085192441025058193581327559331546948442042987131728039318861235625879376246169858586459472691398815098207618446039"); //.BigInteger.probablePrime(N / 2, rnd);
+ BigInteger q = new BigInteger("4311534819291430017572425052029278681302539382618633848168923130451247487970187151403375389974616614405320169278870943605377518341666894603659873284783174749122655429409273983428000534304828056597676444751611433784228298909767"); //BigInteger.probablePrime(N / 2, rnd);
+ BigInteger phi = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE));
+
+ BigInteger n = p.multiply(q);
+ BigInteger e = new BigInteger("65537");
+ BigInteger d = e.modInverse(phi);
+
+ hostkey = new RSAPrivateKey(d, e, n);
+ }
+
+ @Override
+ public RSAPrivateKey getPrivateKey() {
+ return hostkey;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.ssh.threads;
+
+import ch.ethz.ssh2.ServerConnection;
+import ch.ethz.ssh2.ServerSession;
+import java.io.InputStream;
+import java.io.OutputStream;
+import javax.annotation.concurrent.ThreadSafe;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ThreadSafe
+public class IOThread extends Thread {
+
+ private static final Logger logger = LoggerFactory.getLogger(IOThread.class);
+
+ private InputStream inputStream;
+ private OutputStream outputStream;
+ private String id;
+ private ServerSession servSession;
+ private ServerConnection servconnection;
+
+
+ public IOThread (InputStream is, OutputStream os, String id,ServerSession ss, ServerConnection conn){
+ this.inputStream = is;
+ this.outputStream = os;
+ this.servSession = ss;
+ this.servconnection = conn;
+ super.setName(id);
+ logger.trace("IOThread {} created", super.getName());
+ }
+
+ @Override
+ public void run() {
+ logger.trace("thread {} started", super.getName());
+ try {
+ IOUtils.copy(this.inputStream, this.outputStream);
+ } catch (Exception e) {
+ logger.error("inputstream -> outputstream copy error ",e);
+ }
+ logger.trace("closing server session");
+ servSession.close();
+ servconnection.close();
+ logger.trace("thread {} is closing",super.getName());
+ }
+}
--- /dev/null
+package org.opendaylight.controller.netconf.ssh.threads;
+
+
+import ch.ethz.ssh2.AuthenticationResult;
+import ch.ethz.ssh2.PtySettings;
+import ch.ethz.ssh2.ServerAuthenticationCallback;
+import ch.ethz.ssh2.ServerConnection;
+import ch.ethz.ssh2.ServerConnectionCallback;
+import ch.ethz.ssh2.ServerSession;
+import ch.ethz.ssh2.ServerSessionCallback;
+import ch.ethz.ssh2.SimpleServerSessionCallback;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.controller.netconf.ssh.authentication.RSAKey;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ThreadSafe
+public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback
+{
+
+ private Socket socket;
+ private static final String USER = "netconf";
+ private static final String PASSWORD = "netconf";
+ private InetSocketAddress clientAddress;
+ private static final Logger logger = LoggerFactory.getLogger(SocketThread.class);
+ private ServerConnection conn = null;
+ private long sessionId;
+
+
+ public static void start(Socket socket, InetSocketAddress clientAddress, long sessionId) throws IOException{
+ Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket,clientAddress,sessionId));
+ netconf_ssh_socket_thread.setDaemon(true);
+ netconf_ssh_socket_thread.start();
+ }
+ private SocketThread(Socket socket, InetSocketAddress clientAddress, long sessionId) throws IOException {
+
+ this.socket = socket;
+ this.clientAddress = clientAddress;
+ this.sessionId = sessionId;
+
+ }
+
+ @Override
+ public void run() {
+ conn = new ServerConnection(socket);
+ RSAKey keyStore = new RSAKey();
+ conn.setRsaHostKey(keyStore.getPrivateKey());
+ conn.setAuthenticationCallback(this);
+ conn.setServerConnectionCallback(this);
+ try {
+ conn.connect();
+ } catch (IOException e) {
+ logger.error("SocketThread error ",e);
+ }
+ }
+ public ServerSessionCallback acceptSession(final ServerSession session)
+ {
+ SimpleServerSessionCallback cb = new SimpleServerSessionCallback()
+ {
+ @Override
+ public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException
+ {
+ return new Runnable(){
+ public void run()
+ {
+ if (subsystem.equals("netconf")){
+ IOThread netconf_ssh_input = null;
+ IOThread netconf_ssh_output = null;
+ try {
+ String hostName = clientAddress.getHostName();
+ int portNumber = clientAddress.getPort();
+ final Socket echoSocket = new Socket(hostName, portNumber);
+ logger.trace("echo socket created");
+
+ logger.trace("starting netconf_ssh_input thread");
+ netconf_ssh_input = new IOThread(echoSocket.getInputStream(),ss.getStdin(),"input_thread_"+sessionId,ss,conn);
+ netconf_ssh_input.setDaemon(false);
+ netconf_ssh_input.start();
+
+ logger.trace("starting netconf_ssh_output thread");
+ netconf_ssh_output = new IOThread(ss.getStdout(),echoSocket.getOutputStream(),"output_thread_"+sessionId,ss,conn);
+ netconf_ssh_output.setDaemon(false);
+ netconf_ssh_output.start();
+
+ } catch (Throwable t){
+ logger.error(t.getMessage(),t);
+
+ try {
+ if (netconf_ssh_input!=null){
+ netconf_ssh_input.join();
+ }
+ } catch (InterruptedException e) {
+ logger.error("netconf_ssh_input join error ",e);
+ }
+
+ try {
+ if (netconf_ssh_output!=null){
+ netconf_ssh_output.join();
+ }
+ } catch (InterruptedException e) {
+ logger.error("netconf_ssh_output join error ",e);
+ }
+
+ }
+ } else {
+ try {
+ ss.getStdin().write("wrong subsystem requested - closing connection".getBytes());
+ ss.close();
+ } catch (IOException e) {
+ logger.debug("excpetion while sending bad subsystem response",e);
+ }
+ }
+ }
+ };
+ }
+ @Override
+ public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException
+ {
+ return new Runnable()
+ {
+ public void run()
+ {
+ //noop
+ }
+ };
+ }
+
+ @Override
+ public Runnable requestShell(final ServerSession ss) throws IOException
+ {
+ return new Runnable()
+ {
+ public void run()
+ {
+ //noop
+ }
+ };
+ }
+ };
+
+ return cb;
+ }
+
+ public String initAuthentication(ServerConnection sc)
+ {
+ return "";
+ }
+
+ public String[] getRemainingAuthMethods(ServerConnection sc)
+ {
+ return new String[] { ServerAuthenticationCallback.METHOD_PASSWORD };
+ }
+
+ public AuthenticationResult authenticateWithNone(ServerConnection sc, String username)
+ {
+ return AuthenticationResult.FAILURE;
+ }
+
+ public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password)
+ {
+ if (USER.equals(username) && PASSWORD.equals(password))
+ return AuthenticationResult.SUCCESS;
+
+ return AuthenticationResult.FAILURE;
+ }
+
+ public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
+ byte[] publickey, byte[] signature)
+ {
+ return AuthenticationResult.FAILURE;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.ssh;
+
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import junit.framework.Assert;
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class SSHServerTest {
+
+ private static final String USER = "netconf";
+ private static final String PASSWORD = "netconf";
+ private static final String HOST = "127.0.0.1";
+ private static final int PORT = 1830;
+ private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383);
+ private static final Logger logger = LoggerFactory.getLogger(SSHServerTest.class);
+
+// @Before
+ public void startSSHServer() throws Exception{
+ logger.info("Creating SSH server");
+ NetconfSSHServer server = NetconfSSHServer.start(PORT,tcpAddress);
+ Thread sshServerThread = new Thread(server);
+ sshServerThread.setDaemon(true);
+ sshServerThread.start();
+ logger.info("SSH server on");
+ }
+
+ @Test
+ public void connect(){
+ Connection conn = new Connection(HOST,PORT);
+ Assert.assertNotNull(conn);
+ try {
+ logger.info("connecting to SSH server");
+ conn.connect();
+ logger.info("authenticating ...");
+ boolean isAuthenticated = conn.authenticateWithPassword(USER,PASSWORD);
+ Assert.assertTrue(isAuthenticated);
+ logger.info("opening session");
+ Session sess = conn.openSession();
+ logger.info("subsystem netconf");
+ sess.startSubSystem("netconf");
+ sess.getStdin().write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>".getBytes());
+ IOUtils.copy(sess.getStdout(), System.out);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
org.opendaylight.controller.netconf.util.mapping,
org.opendaylight.controller.netconf.util.messages,
org.opendaylight.controller.netconf.util.handler,
+ org.opendaylight.controller.netconf.util.handler.*,
</Export-Package>
<Import-Package>
com.google.common.base,
package org.opendaylight.controller.netconf.util.handler.ssh;
+import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
+
import java.io.IOException;
import java.net.SocketAddress;
+
import org.opendaylight.controller.netconf.util.handler.ssh.authentication.AuthenticationHandler;
import org.opendaylight.controller.netconf.util.handler.ssh.client.Invoker;
import org.opendaylight.controller.netconf.util.handler.ssh.client.SshClient;
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
- this.sshClientAdapter.write((String) msg);
+ this.sshClientAdapter.write((ByteBuf) msg);
}
@Override
import java.io.IOException;
/**
- * Class Providing username/password authentication option to {@link org.opendaylight.controller.netconf.util.handler.ssh.SshHandler}
+ * Class Providing username/password authentication option to
+ * {@link org.opendaylight.controller.netconf.util.handler.ssh.SshHandler}
*/
public class LoginPassword extends AuthenticationHandler {
private final String username;
public void authenticate(Connection connection) throws IOException {
boolean isAuthenticated = connection.authenticateWithPassword(username, password);
- if (isAuthenticated == false) throw new IOException("Authentication failed.");
+ if (isAuthenticated == false)
+ throw new IOException("Authentication failed.");
}
}
import java.util.HashMap;
import java.util.Map;
-
/**
* Wrapper class around GANYMED SSH java library.
*/
private final AuthenticationHandler authenticationHandler;
private Connection connection;
- public SshClient(VirtualSocket socket,
- AuthenticationHandler authenticationHandler) throws IOException {
+ public SshClient(VirtualSocket socket, AuthenticationHandler authenticationHandler) throws IOException {
this.socket = socket;
this.authenticationHandler = authenticationHandler;
}
public SshSession openSession() throws IOException {
- if(connection == null) connect();
+ if (connection == null)
+ connect();
- Session session = connection.openSession();
+ Session session = connection.openSession();
SshSession sshSession = new SshSession(session);
openSessions.put(openSessions.size(), sshSession);
private void connect() throws IOException {
connection = new Connection(socket);
+
connection.connect();
authenticationHandler.authenticate(connection);
}
public void closeSession(SshSession session) {
- if( session.getState() == Channel.STATE_OPEN
- || session.getState() == Channel.STATE_OPENING) {
+ if (session.getState() == Channel.STATE_OPEN || session.getState() == Channel.STATE_OPENING) {
session.session.close();
}
}
public void close() {
- for(SshSession session : openSessions.values()) closeSession(session);
+ for (SshSession session : openSessions.values())
+ closeSession(session);
openSessions.clear();
- if(connection != null) connection.close();
+ if (connection != null)
+ connection.close();
}
}
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.LinkedList;
+import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;
import org.opendaylight.controller.netconf.util.handler.ssh.virtualsocket.VirtualSocketException;
+
/**
- * Worker thread class. Handles all downstream and upstream events in SSH Netty pipeline.
+ * Worker thread class. Handles all downstream and upstream events in SSH Netty
+ * pipeline.
*/
public class SshClientAdapter implements Runnable {
private final SshClient sshClient;
private InputStream stdErr;
private OutputStream stdIn;
+ private Queue<ByteBuf> postponned = new LinkedList<>();
+
+
private ChannelHandlerContext ctx;
private ChannelPromise disconnectPromise;
private final Object lock = new Object();
- public SshClientAdapter(SshClient sshClient,
- Invoker invoker) {
+ public SshClientAdapter(SshClient sshClient, Invoker invoker) {
this.sshClient = sshClient;
this.invoker = invoker;
}
try {
session = sshClient.openSession();
invoker.invoke(session);
-
stdOut = session.getStdout();
stdErr = session.getStderr();
- synchronized(lock) {
+ synchronized (lock) {
+
stdIn = session.getStdin();
+ ByteBuf message = null;
+ while ((message = postponned.poll()) != null) {
+ writeImpl(message);
+ }
}
while (stopRequested.get() == false) {
byte[] readBuff = new byte[1024];
int c = stdOut.read(readBuff);
-
+ if (c == -1) {
+ continue;
+ }
byte[] tranBuff = new byte[c];
System.arraycopy(readBuff, 0, tranBuff, 0, c);
sshClient.close();
synchronized (lock) {
- if(disconnectPromise != null) ctx.disconnect(disconnectPromise);
+ if (disconnectPromise != null)
+ ctx.disconnect(disconnectPromise);
}
}
}
// TODO: needs rework to match netconf framer API.
- public void write(String message) throws IOException {
+ public void write(ByteBuf message) throws IOException {
synchronized (lock) {
- if (stdIn == null) throw new IllegalStateException("StdIn not available");
+ if (stdIn == null) {
+ postponned.add(message);
+ return;
+ }
+ writeImpl(message);
}
- stdIn.write(message.getBytes());
+ }
+
+ private void writeImpl(ByteBuf message) throws IOException {
+ message.getBytes(0, stdIn, message.readableBytes());
stdIn.flush();
}
}
public void start(ChannelHandlerContext ctx) {
- if(this.ctx != null) return; // context is already associated.
-
+ if (this.ctx != null)
+ return; // context is already associated.
this.ctx = ctx;
new Thread(this).start();
}
private static final String PREFIX_PROP = "netconf.";
private enum InfixProp {
- tcp, tls
+ tcp, tls, ssh
}
private static final String PORT_SUFFIX_PROP = ".port";
private static final String NETCONF_TLS_KEYSTORE_PROP = PREFIX_PROP + InfixProp.tls + ".keystore";
private static final String NETCONF_TLS_KEYSTORE_PASSWORD_PROP = NETCONF_TLS_KEYSTORE_PROP + ".password";
- public static Optional<InetSocketAddress> extractTCPNetconfAddress(BundleContext context) {
- return extractSomeNetconfAddress(context, InfixProp.tcp);
+ public static InetSocketAddress extractTCPNetconfAddress(BundleContext context, String exceptionMessageIfNotFound) {
+
+ Optional<InetSocketAddress> inetSocketAddressOptional = extractSomeNetconfAddress(context, InfixProp.tcp);
+ if (inetSocketAddressOptional.isPresent() == false) {
+ throw new IllegalStateException("Netconf tcp address not found." + exceptionMessageIfNotFound);
+ }
+ return inetSocketAddressOptional.get();
}
+ public static Optional<InetSocketAddress> extractSSHNetconfAddress(BundleContext context) {
+ return extractSomeNetconfAddress(context, InfixProp.ssh);
+ }
+
+
public static Optional<TLSConfiguration> extractTLSConfiguration(BundleContext context) {
Optional<InetSocketAddress> address = extractSomeNetconfAddress(context, InfixProp.tls);
if (address.isPresent()) {
<name>test1</name>
+ <extended>
+ 1
+ </extended>
+
+ <extended-twice>
+ 1
+ </extended-twice>
+
+ <extended-enum>
+ TWO
+ </extended-enum>
+
<simple-long-2>44</simple-long-2>
<binaryLeaf>8</binaryLeaf>
<binaryLeaf>1</binaryLeaf>
test-impl:impl-netconf
</type>
<name>test2</name>
+ <simple-short>4</simple-short>
+
<testing-dep>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<name>ref_dep</name>
<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<no-arg xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
- <context-instance>/modules/module[type='impl-netconf' and name='instance']</context-instance>
+ <context-instance>/modules/module[type='impl-netconf' and name='test1']</context-instance>
<arg1>
testarg1
</arg1>
<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<noArgInner xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
<context-instance>
- /modules/module[name='instance2'][type='impl-netconf']/inner-running-data-additional[key='randomString_1003']
+ /modules/module[name='test1'][type='impl-netconf']/inner-running-data-additional[key='1']
</context-instance>
</noArgInner>
</rpc>
<noArgInnerInner
xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
<context-instance>
- /modules/module[type='impl-netconf'][name='instance2']/inner-running-data[key='1015']/inner-inner-running-data[key='1017']
+ /modules/module[type='impl-netconf'][name='test2']/inner-running-data[key='2']/inner-inner-running-data[key='3']
</context-instance>
<arg1>
<leaf-list-output
xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
<context-instance>
- /modules/module[type='impl-netconf'][name='instance2']/inner-running-data[key='1015']/inner-inner-running-data[key='1017']
+ /modules/module[type='impl-netconf'][name='test1']/inner-running-data[key='0']/inner-inner-running-data[key='1']
</context-instance>
</leaf-list-output>
</rpc>
<module>config-persister-impl</module>
<module>netconf-mapping-api</module>
<module>netconf-client</module>
+ <module>netconf-ssh</module>
<module>../../third-party/ganymed</module>
<module>../../third-party/com.siemens.ct.exi</module>
</modules>
<version>${netconf.version}</version>
<type>test-jar</type>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-ssh</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-mapping-api</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
- <Export-Package>ch.ethz.ssh2</Export-Package>
+ <Export-Package>ch.ethz.ssh2.*</Export-Package>
<Embed-Dependency>ganymed-ssh2;scope=compile</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
</instructions>