This implementation is based on infrastructure provided by the framework artifact
and contains server as well as client side code.
Netconf subsystem structure:
Netconf-api:
Api definition for client and server.
Netconf-impl:
Netconf server implementation. Server handles basic communication
and delegates handling of rpcs to an implementation of netconf-mapping-api.
Netconf-mapping-api:
Api definition for pluggable rpcs handler. Implementations of this api
are plugged dynamically into netconf-impl using OSGi apis.
Config-netconf-connector:
Implementation of netconf-mapping-api that handles netconf rpcs and delegates
requests to configuration subsystem
Netconf-util:
Utility classes used by client and server code.
Netconf-client:
Netconf client implementation.
Netconf-it:
Integration tests for netconf. These tests verify correct cooperation of netconf server,
config-netconf-connector, config subsystem and netconf client.
Config-persister-api:
Api definition for config persister. Config persister is a component that is responsible
for storing configuration snapshots after every change to the configuration via netconf rpcs.
(Pushed to config-subsystem)
Config-persister-impl:
Implementation of config persister that receives notifications from netconf-impl about changes
to the configuration and uses pluggable adapters to store received snapshots.
Config-persister-file-adapter:
Implementation of config persister adapter that stores and restores config snapshots from a file.
(Pushed to config-subsystem)
In order to run netconf in OSGi, some configuration issues had to be resolved
in config-subsystem bundles.
Change-Id: I8e0421c924b0714a4d49962c4bb5ca01ef68ac78
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
<geminiweb.version>2.2.0.RELEASE</geminiweb.version>
<checkstyle.version>2.10</checkstyle.version>
<testvm.argLine>-Xmx1024m -XX:MaxPermSize=256m</testvm.argLine>
- <yang.version>0.5.8</yang.version>
+ <yang.version>0.5.9-SNAPSHOT</yang.version>
<guava.version>14.0.1</guava.version>
<osgi.core.version>5.0.0</osgi.core.version>
<ietf-inet-types.version>2010.09.24.1</ietf-inet-types.version>
<bundle.plugin.version>2.3.7</bundle.plugin.version>
<junit.version>4.8.1</junit.version>
<bgpcep.version>0.2.0-SNAPSHOT</bgpcep.version>
- <yangtools.version>0.5.8</yangtools.version>
+ <yangtools.version>0.5.9-SNAPSHOT</yangtools.version>
+ <yangtools.binding.version>0.6.0-SNAPSHOT</yangtools.binding.version>
<!--versions for bits of the controller -->
<controller.version>0.4.1-SNAPSHOT</controller.version>
<config.version>0.2.1-SNAPSHOT</config.version>
<version>${bgpcep.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>framework</artifactId>
+ <version>${bgpcep.version}</version>
+ </dependency>
+
+ <!--Netty-->
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-handler</artifactId>
+ <version>4.0.9.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec</artifactId>
+ <version>4.0.9.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-buffer</artifactId>
+ <version>4.0.9.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-transport</artifactId>
+ <version>4.0.9.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-common</artifactId>
+ <version>4.0.9.Final</version>
+ </dependency>
+
<!-- yangtools dependencies -->
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-binding</artifactId>
- <version>${yangtools.version}</version>
+ <version>${yangtools.binding.version}</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>concepts</artifactId>
<version>0.2.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
</dependencies>
<build>
<instructions>
<Import-Package>
javax.management,
- org.opendaylight.protocol.concepts
+ org.opendaylight.protocol.concepts,
+ org.osgi.framework,
</Import-Package>
<Export-Package>
org.opendaylight.controller.config.api,
org.opendaylight.controller.config.api.jmx,
org.opendaylight.controller.config.api.jmx.constants,
org.opendaylight.controller.config.api.runtime,
+ org.opendaylight.controller.config.stat,
</Export-Package>
</instructions>
</configuration>
--- /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.stat;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * Subset of {@link org.osgi.framework.BundleContext}
+ */
+public interface ConfigProvider {
+ /**
+ * Returns the value of the specified property. If the key is not found in
+ * the Framework properties, the system properties are then searched. The
+ * method returns {@code null} if the property is not found.
+ *
+ * <p>
+ * All bundles must have permission to read properties whose names start
+ * with "org.osgi.".
+ *
+ * @param key
+ * The name of the requested property.
+ * @return The value of the requested property, or {@code null} if the
+ * property is undefined.
+ * @throws SecurityException
+ * If the caller does not have the appropriate
+ * {@code PropertyPermission} to read the property, and the Java
+ * Runtime Environment supports permissions.
+ */
+ String getProperty(String key);
+
+ public static class ConfigProviderImpl implements ConfigProvider {
+ private final BundleContext context;
+
+ public ConfigProviderImpl(BundleContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return context.getProperty(key);
+ }
+
+ @Override
+ public String toString() {
+ return "ConfigProviderImpl{" + "context=" + context + '}';
+ }
+ }
+
+}
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>config-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>config-persister-api</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-util</artifactId>
+ <version>0.2.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ com.google.common.base,
+ org.w3c.dom,
+ org.osgi.framework,
+ org.opendaylight.controller.config.stat
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.controller.config.persist.api,
+ org.opendaylight.controller.config.persist.api.storage,
+ </Export-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.api;
+
+import com.google.common.base.Optional;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Base interface for persister implementation.
+ */
+public interface Persister extends Closeable {
+
+ void persistConfig(ConfigSnapshotHolder configSnapshotHolder) throws IOException;
+
+ Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException;
+
+ public static interface ConfigSnapshotHolder {
+
+ 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.storage;
+
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+
+/**
+ * Plugins for {@link org.opendaylight.controller.config.persist.api.Persister}
+ * must implement this interface.
+ */
+public interface StorageAdapter extends Persister {
+
+ void setProperties(ConfigProvider configProvider);
+
+}
--- /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.1-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>config-persister-file-adapter</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-persister-api</artifactId>
+ <version>${project.version}</version>
+ </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>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <version>0.2.0-SNAPSHOT</version>
+ <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>
+ org.osgi.framework,
+ com.google.common.base,
+ com.google.common.collect,
+ com.google.common.io,
+ javax.xml.parsers,
+ javax.xml.transform,
+ javax.xml.transform.dom,
+ javax.xml.transform.stream,
+ org.apache.commons.lang3,
+ org.opendaylight.controller.config.persist.api,
+ org.opendaylight.controller.config.stat,
+ org.opendaylight.controller.config.persist.api.storage,
+ 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.config.persist.storage.file;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import org.apache.commons.lang3.StringUtils;
+import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Set;
+
+/**
+ * StorageAdapter that stores configuration in a plan file.
+ */
+public class FileStorageAdapter implements StorageAdapter {
+ 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 static final String SEPARATOR_M_PURE = "//END OF SNAPSHOT";
+ private static final String SEPARATOR_M = newLine(SEPARATOR_M_PURE);
+
+ private static final String SEPARATOR_S = newLine("//START OF CONFIG");
+
+ private static final String SEPARATOR_SL_PURE = "//START OF CONFIG-LAST";
+ private static final String SEPARATOR_SL = newLine(SEPARATOR_SL_PURE);
+
+ private static Integer numberOfStoredBackups;
+ private File storage;
+
+ @Override
+ public void setProperties(ConfigProvider configProvider) {
+ File storage = extractStorageFileFromProperties(configProvider);
+ logger.debug("Using file {}", storage.getAbsolutePath());
+ // Create file if it does not exist
+ File parentFile = storage.getAbsoluteFile().getParentFile();
+ if (parentFile.exists() == false) {
+ logger.debug("Creating parent folders {}", parentFile);
+ parentFile.mkdirs();
+ }
+ if (storage.exists() == false) {
+ logger.debug("Storage file does not exist, creating empty file");
+ try {
+ boolean result = storage.createNewFile();
+ if (result == false)
+ throw new RuntimeException("Unable to create storage file " + storage);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to create storage file " + storage, e);
+ }
+ }
+ if (numberOfStoredBackups == 0) {
+ throw new RuntimeException(NUMBER_OF_BACKUPS
+ + " property should be either set to positive value, or ommited. Can not be set to 0.");
+ }
+ setFileStorage(storage);
+
+ }
+
+ @VisibleForTesting
+ void setFileStorage(File storage) {
+ this.storage = storage;
+ }
+
+ @VisibleForTesting
+ void setNumberOfBackups(Integer numberOfBackups) {
+ numberOfStoredBackups = numberOfBackups;
+ }
+
+ private static File extractStorageFileFromProperties(ConfigProvider configProvider) {
+ String fileStorageProperty = configProvider.getProperty(FILE_STORAGE_PROP);
+ Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + FILE_STORAGE_PROP
+ + " in received properties :" + configProvider);
+ File result = new File(fileStorageProperty);
+ String numberOfBAckupsAsString = configProvider.getProperty(NUMBER_OF_BACKUPS);
+ if (numberOfBAckupsAsString != null) {
+ numberOfStoredBackups = Integer.valueOf(numberOfBAckupsAsString);
+ } else {
+ numberOfStoredBackups = Integer.MAX_VALUE;
+ }
+
+ return result;
+ }
+
+ private static String newLine(String string) {
+ return string + "\n";
+ }
+
+ @Override
+ public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
+ Preconditions.checkNotNull(storage, "Storage file is null");
+
+ String content = Files.toString(storage, ENCODING);
+ if (numberOfStoredBackups == Integer.MAX_VALUE) {
+ resetLastConfig(content);
+ persistLastConfig(holder);
+ } else {
+ if (numberOfStoredBackups == 1) {
+ Files.write("", storage, ENCODING);
+ persistLastConfig(holder);
+ } else {
+ int count = StringUtils.countMatches(content, SEPARATOR_S);
+ if ((count + 1) < numberOfStoredBackups) {
+ resetLastConfig(content);
+ persistLastConfig(holder);
+ } else {
+ String contentSubString = StringUtils.substringBefore(content, SEPARATOR_E);
+ contentSubString = contentSubString.concat(SEPARATOR_E_PURE);
+ content = StringUtils.substringAfter(content, contentSubString);
+ resetLastConfig(content);
+ persistLastConfig(holder);
+ }
+ }
+ }
+ }
+
+ private void resetLastConfig(String content) throws IOException {
+ content = content.replaceFirst(SEPARATOR_SL, SEPARATOR_S);
+ Files.write(content, storage, ENCODING);
+ }
+
+ private void persistLastConfig(ConfigSnapshotHolder holder) throws IOException {
+ Files.append(SEPARATOR_SL, storage, ENCODING);
+ String snapshotAsString = holder.getConfigSnapshot();
+ Files.append(newLine(snapshotAsString), storage, ENCODING);
+ Files.append(SEPARATOR_M, storage, ENCODING);
+ Files.append(toStringCaps(holder.getCapabilities()), storage, ENCODING);
+ Files.append(SEPARATOR_E, storage, ENCODING);
+ }
+
+ private CharSequence toStringCaps(Set<String> capabilities) {
+ StringBuilder b = new StringBuilder();
+ for (String capability : capabilities) {
+ b.append(newLine(capability));
+ }
+ return b.toString();
+ }
+
+ @Override
+ public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ Preconditions.checkNotNull(storage, "Storage file is null");
+
+ if (!storage.exists()) {
+ return Optional.absent();
+ }
+
+ final LineProcessor lineProcessor = new LineProcessor();
+ String result = Files.readLines(storage, ENCODING, lineProcessor);
+
+ try {
+ if (lineProcessor.getConfigSnapshot().isPresent() == false) {
+ return Optional.absent();
+ } else {
+ return Optional.<ConfigSnapshotHolder> of(new PersistedConfigImpl(lineProcessor.getConfigSnapshot(),
+ lineProcessor.getCapabilities()));
+ }
+
+ } catch (ParserConfigurationException | SAXException e) {
+ throw new IOException("Unable to load last config ", e);
+ }
+ }
+
+ private static final class LineProcessor implements com.google.common.io.LineProcessor<String> {
+
+ private boolean inLastConfig, inLastSnapshot;
+ private final StringBuffer snapshotBuffer = new StringBuffer();
+ private final Set<String> caps = Sets.newHashSet();
+
+ @Override
+ public String getResult() {
+ return null;
+ }
+
+ @Override
+ public boolean processLine(String line) throws IOException {
+ if (inLastConfig && line.equals(SEPARATOR_E_PURE)) {
+ inLastConfig = false;
+ return false;
+ }
+
+ if (inLastConfig && line.equals(SEPARATOR_M_PURE)) {
+ inLastSnapshot = false;
+ return true;
+ }
+
+ if (inLastConfig) {
+ if (inLastSnapshot) {
+ snapshotBuffer.append(line);
+ snapshotBuffer.append(System.lineSeparator());
+ } else {
+ caps.add(line);
+ }
+ }
+
+ if (line.equals(SEPARATOR_SL_PURE)) {
+ inLastConfig = true;
+ inLastSnapshot = true;
+ }
+
+ return true;
+ }
+
+ Optional<String> getConfigSnapshot() throws IOException, SAXException, ParserConfigurationException {
+ final String xmlContent = snapshotBuffer.toString();
+ if (xmlContent == null || xmlContent.equals("")) {
+ return Optional.absent();
+ } else
+ return Optional.of(xmlContent);
+ }
+
+ Set<String> getCapabilities() throws IOException, SAXException, ParserConfigurationException {
+ return caps;
+ }
+
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+
+ @Override
+ public String toString() {
+ return "FileStorageAdapter [storage=" + storage + "]";
+ }
+
+ private class PersistedConfigImpl implements ConfigSnapshotHolder {
+
+ private final String snapshot;
+ private final Set<String> caps;
+
+ public PersistedConfigImpl(Optional<String> configSnapshot, Set<String> capabilities) {
+ this.snapshot = configSnapshot.get();
+ this.caps = capabilities;
+ }
+
+ @Override
+ public String getConfigSnapshot() {
+ return snapshot;
+ }
+
+ @Override
+ public Set<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.file;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import 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 java.io.File;
+import java.nio.file.Files;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+
+public class FileStorageAdapterTest {
+
+ private static int i;
+ private File file;
+
+ @Before
+ public void setUp() throws Exception {
+ file = Files.createTempFile("testFilePersist", ".txt").toFile();
+ if (!file.exists())
+ return;
+ com.google.common.io.Files.write("", file, Charsets.UTF_8);
+ i = 1;
+ }
+
+ @Test
+ public void testFileAdapter() throws Exception {
+ FileStorageAdapter storage = new FileStorageAdapter();
+ storage.setFileStorage(file);
+ storage.setNumberOfBackups(Integer.MAX_VALUE);
+ final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() {
+ @Override
+ public String getConfigSnapshot() {
+ return createConfig();
+ }
+
+ @Override
+ public Set<String> getCapabilities() {
+ return createCaps();
+ }
+ };
+ storage.persistConfig(holder);
+
+ storage.persistConfig(holder);
+
+ Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
+ new Predicate<String>() {
+
+ @Override
+ public boolean apply(String input) {
+ if (input.equals(""))
+ return false;
+ return true;
+ }
+ });
+ assertEquals(14, readLines.size());
+
+ Optional<Persister.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();
+
+ caps.add("cap1");
+ caps.add("cap2");
+ caps.add("capaaaa as dasfasdf s2");
+ return caps;
+ }
+
+ @Test
+ public void testFileAdapterOneBackup() throws Exception {
+ FileStorageAdapter storage = new FileStorageAdapter();
+ storage.setFileStorage(file);
+ storage.setNumberOfBackups(1);
+ final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() {
+ @Override
+ public String getConfigSnapshot() {
+ return createConfig();
+ }
+
+ @Override
+ public Set<String> getCapabilities() {
+ return createCaps();
+ }
+ };
+ storage.persistConfig(holder);
+
+ storage.persistConfig(holder);
+
+ Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
+ new Predicate<String>() {
+
+ @Override
+ public boolean apply(String input) {
+ if (input.equals(""))
+ return false;
+ return true;
+ }
+ });
+ assertEquals(7, readLines.size());
+
+ Optional<Persister.ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
+ assertTrue(lastConf.isPresent());
+ assertEquals("<config>2</config>",
+ lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
+ }
+
+ @Test
+ public void testFileAdapterOnlyTwoBackups() throws Exception {
+ FileStorageAdapter storage = new FileStorageAdapter();
+ storage.setFileStorage(file);
+ storage.setNumberOfBackups(2);
+ final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() {
+ @Override
+ public String getConfigSnapshot() {
+ return createConfig();
+ }
+
+ @Override
+ public Set<String> getCapabilities() {
+ return createCaps();
+ }
+ };
+ storage.persistConfig(holder);
+
+ storage.persistConfig(holder);
+ storage.persistConfig(holder);
+
+ Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
+ new Predicate<String>() {
+
+ @Override
+ public boolean apply(String input) {
+ if (input.equals(""))
+ return false;
+ return true;
+ }
+ });
+
+ assertEquals(14, readLines.size());
+
+ Optional<Persister.ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
+ assertTrue(lastConf.isPresent());
+ assertEquals("<config>3</config>",
+ lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
+ assertFalse(readLines.contains(holder.getConfigSnapshot()));
+ }
+
+ @Test
+ public void testNoLastConfig() throws Exception {
+ File file = Files.createTempFile("testFilePersist", ".txt").toFile();
+ if (!file.exists())
+ return;
+ FileStorageAdapter storage = new FileStorageAdapter();
+ storage.setFileStorage(file);
+
+ Optional<Persister.ConfigSnapshotHolder> elementOptional = storage.loadLastConfig();
+ assertThat(elementOptional.isPresent(), is(false));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNoProperties() throws Exception {
+ FileStorageAdapter storage = new FileStorageAdapter();
+ storage.loadLastConfig();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNoProperties2() throws Exception {
+ FileStorageAdapter storage = new FileStorageAdapter();
+ storage.persistConfig(new Persister.ConfigSnapshotHolder() {
+ @Override
+ public String getConfigSnapshot() {
+ return Mockito.mock(String.class);
+ }
+
+ @Override
+ public Set<String> getCapabilities() {
+ return Collections.<String> emptySet();
+ }
+ } );
+ }
+
+ static String createConfig() {
+ return "<config>" + i++ + "</config>";
+ }
+
+}
<module>config-api</module>
<module>config-manager</module>
<module>config-util</module>
+ <module>config-persister-api</module>
+ <module>config-persister-file-adapter</module>
<module>yang-jmx-generator</module>
<module>yang-jmx-generator-plugin</module>
<module>yang-jmx-generator-it</module>
<jacoco.version>0.6.2.201302030002</jacoco.version>
<slf4j.version>1.7.2</slf4j.version>
<jolokia.version>1.1.1</jolokia.version>
- <osgi.core.version>5.0.0</osgi.core.version>
<opendaylight.yang.version>0.5.9-SNAPSHOT</opendaylight.yang.version>
<opendaylight.binding.version>0.6.0-SNAPSHOT</opendaylight.binding.version>
<jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
<artifactId>gmaven-plugin</artifactId>
<version>1.0</version>
</plugin>
+ <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>
+ org.opendaylight.yangtools
+ </groupId>
+ <artifactId>
+ yang-maven-plugin
+ </artifactId>
+ <versionRange>
+ [0.5.7-SNAPSHOT,)
+ </versionRange>
+ <goals>
+ <goal>
+ generate-sources
+ </goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
</plugins>
</pluginManagement>
</build>
<pluginRepositories>
<pluginRepository>
<id>nexus.opendaylight.org</id>
- <url>http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot</url>
+ <url>http://nexus.opendaylight.org/content/repositories/opendaylight.release</url>
<releases>
<enabled>true</enabled>
</releases>
</parent>
<artifactId>yang-jmx-generator-plugin</artifactId>
<dependencies>
+
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
+
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>yang-jmx-generator</artifactId>
<version>0.2.1-SNAPSHOT</version>
</dependency>
+
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin-spi</artifactId>
<version>${opendaylight.yang.version}</version>
</dependency>
+
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-generator-impl</artifactId>
+ <artifactId>binding-type-provider</artifactId>
<version>${opendaylight.binding.version}</version>
</dependency>
+
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>core</artifactId>
<version>3.3.0-v_771</version>
<scope>test</scope>
</dependency>
+
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
+
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>config-api</artifactId>
<version>0.2.1-SNAPSHOT</version>
</dependency>
+
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
+
<dependency>
<!--FIXME two implementations of slf4j on classpath, logback classic from parent-->
<groupId>com.googlecode.slf4j-maven-plugin-log</groupId>
<artifactId>slf4j-maven-plugin-log</artifactId>
<version>1.0.0</version>
</dependency>
+
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
+
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>jdt</artifactId>
<version>3.3.0-v20070607-1300</version>
<scope>test</scope>
</dependency>
+
<dependency>
<groupId>org.opendaylight.bgpcep</groupId>
<artifactId>mockito-configuration</artifactId>
<version>0.2.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
+
<dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
*/
package org.opendaylight.controller.config.yangjmxgenerator.plugin;
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.slf4j.LoggerFactory;
import org.slf4j.impl.StaticLoggerBinder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* This class interfaces with yang-maven-plugin. Gets parsed yang modules in
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
+
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-generator-util</artifactId>
+ <artifactId>binding-generator-spi</artifactId>
<version>${opendaylight.binding.version}</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-generator-spi</artifactId>
+ <artifactId>binding-generator-util</artifactId>
<version>${opendaylight.binding.version}</version>
</dependency>
+
+
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
*/
package org.opendaylight.controller.config.yangjmxgenerator;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.model.api.*;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import com.google.common.collect.Lists;
+import java.util.*;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
/**
* Holds information about runtime bean to be generated. There are two kinds of
+ "Error occured in " + rpcDefinition);
}
List<JavaAttribute> parameters = new ArrayList<>();
- for (DataSchemaNode childNode : rpcDefinition.getInput()
- .getChildNodes()) {
+ for (DataSchemaNode childNode : sortAttributes(rpcDefinition.getInput()
+ .getChildNodes())) {
if (childNode.isAddedByUses() == false) { // skip
// refined
// context-instance
attributes, rpcs);
}
+ private static Collection<DataSchemaNode> sortAttributes(Set<DataSchemaNode> childNodes) {
+ final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
+ @Override
+ public int compare(DataSchemaNode o1, DataSchemaNode o2) {
+ return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
+ }
+ });
+ dataSchemaNodes.addAll(childNodes);
+ return dataSchemaNodes;
+ }
+
private static boolean isInnerStateBean(DataSchemaNode child) {
for (UnknownSchemaNode unknownSchemaNode : child
.getUnknownSchemaNodes()) {
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-generator-impl</artifactId>
+ <artifactId>binding-type-provider</artifactId>
<version>${opendaylight.binding.version}</version>
</dependency>
<dependency>
--- /dev/null
+
+#24
+<rpc message-id="101" xm
+#28
+lns="urn:ietf:params:xml:ns:
+#2
+ne
+#33
+tconf:base:1.0">
+ <my-own-method
+#3
+ xm
+#13
+lns="http://e
+#34
+xample.net/me/my-own/1.0">
+ <my
+#8
+-first-p
+#21
+arameter>14</my-first
+#26
+-parameter>
+ <another-p
+#23
+arameter>fred</another-
+#31
+parameter>
+ </my-own-method>
+ <
+#2
+/r
+#3
+pc>
+##
--- /dev/null
+
+#22
+<rpc message-id="101"
+#24
+xmlns="urn:ietf:params:x
+#15
+ml:ns:netconf:b
+#54
+ase:1.0">
+ <get-config>
+ <source>
+ <r
+#2
+un
+#9
+ning/>
+
+#18
+ </source> <fi
+#33
+lter type="subtree">
+ <top x
+#4
+mlns
+#31
+="http://example.com/schema/1.2
+#15
+/config">
+
+#19
+ <users>
+
+#8
+ <us
+#3
+er/
+#5
+>
+
+#77
+ </users>
+ </top>
+ </filter>
+ </g
+#17
+et-config>
+</rpc>
+##
--- /dev/null
+
+#43
+<rpc message-id="101" xmlns="urn:ietf:param
+#14
+s:xml:ns:netco
+#14
+nf:base:1.0">
+
+#26
+<get-config>
+ <source>
+
+#35
+ <running/>
+ </source>
+
+#39
+ <filter type="subtree">
+ <
+#40
+top xmlns="http://example.com/schema/1.2
+#26
+/config">
+ <users>
+
+#36
+ <user>
+ <name>f
+#56
+red</name>
+ </user>
+ </users>
+
+#28
+ </top>
+ </fil
+#1
+t
+#28
+er>
+ </get-config>
+ </rpc>
+##
--- /dev/null
+
+#17
+<rpc message-id="
+#25
+101" xmlns="urn:ietf:para
+#19
+ms:xml:ns:netconf:b
+#3
+ase
+#19
+:1.0">
+ <get-confi
+#61
+g>
+ <source>
+ <running/>
+ </source>
+
+#43
+ <filter type="subtree">
+ <!-- requ
+#13
+est a text ve
+#22
+rsion of the configura
+#9
+tion -->
+
+#16
+ <config-text
+#45
+xmlns="http://example.com/text/1.2/config"/>
+
+#22
+ </filter>
+ </
+#18
+get-config>
+</rpc>
+##
--- /dev/null
+
+#43
+<rpc message-id="101" xmlns="urn:ietf:param
+#41
+s:xml:ns:netconf:base:1.0">
+ <edit-confi
+#29
+g>
+ <target>
+ <ru
+#13
+nning/>
+ <
+#4
+/tar
+#18
+get>
+ <config>
+
+#41
+ <top xmlns="http://example.com/schema/1
+#32
+.2/config">
+ <interface>
+
+#29
+ <name>Ethernet0/0</na
+#30
+me>
+ <mtu>1500</mtu>
+
+#61
+</interface>
+ </top>
+ </config>
+ </e
+#9
+dit-confi
+#9
+g>
+</rpc>
+##
--- /dev/null
+curl http://localhost:17777/jolokia/read/org.opendaylight.controller:instanceName=fixed1,type=ConfigBean,interfaceName=testing-threadpool | jsonpp
+{
+ "request": {
+ "mbean": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean",
+ "type": "read"
+ },
+ "status": 200,
+ "timestamp": 1362416252,
+ "value": {
+ "ExportedInterfaces": [
+ "testing-threadpool",
+ "modifiable-threadpool"
+ ],
+ "ImplementationName": "fixed",
+ "ThreadCount": 10,
+ "TriggerNewInstanceCreation": false
+ }
+}
\ No newline at end of file
--- /dev/null
+$ curl 'http://localhost:17777/jolokia/exec/org.opendaylight.controller:type=ConfigRegistry/lookupConfigBeans()' | jsonpp
+{
+ "request": {
+ "mbean": "org.opendaylight.controller:type=ConfigRegistry",
+ "operation": "lookupConfigBeans()",
+ "type": "exec"
+ },
+ "status": 200,
+ "timestamp": 1362417043,
+ "value": [
+ {
+ "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=modifiable-threadpool,type=ConfigBean"
+ },
+ {
+ "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean"
+ }
+ ]
+}
--- /dev/null
+<rpc message-id="104"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <commit/>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="102"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <lock>
+ <target>
+ <candidate/>
+ </target>
+ </lock>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="101"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <lock>
+ <target>
+ <running/>
+ </target>
+ </lock>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="103"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>none</default-operation>
+ <test-option>test-then-set</test-option>
+ <error-option>stop-on-error</error-option>
+ <nc:config
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ xmlns="uri-for-my-data-model-namespace">
+ <some-existing-node>
+ <my-new-node nc:operation="create">
+ <my-new-leaf>7</my-new-leaf>
+ </my-new-node>
+ </some-existing-node>
+ </nc:config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="105"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <unlock>
+ <target>
+ <candidate/>
+ </target>
+ </unlock>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="106"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <unlock>
+ <target>
+ <running/>
+ </target>
+ </unlock>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <rpc-error>
+ <error-type>rpc</error-type>
+ <error-tag>missing-attribute</error-tag>
+ <error-severity>error</error-severity>
+ <error-info>
+ <bad-attribute>message-id</bad-attribute>
+ <bad-element>rpc</bad-element>
+ </error-info>
+ </rpc-error>
+</rpc-reply>
<version>0.1.1-SNAPSHOT</version>
</dependency>
- <!-- config
+ <!-- config-->
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-api</artifactId>
<artifactId>logback-config</artifactId>
<version>${config.version}</version>
</dependency>
- -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-api</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-file-adapter</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+
+
+ <!-- Netconf -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-api</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-impl</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-util</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-client</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-netconf-connector</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-impl</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+
+
</dependencies>
</profile>
</profiles>
<groupId>org.opendaylight.bgpcep</groupId>
<artifactId>util</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>framework</artifactId>
+ </dependency>
- <!-- testing dependencies I'm pretty sure we should trim -->
+ <!--Netty-->
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-handler</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-buffer</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-transport</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-common</artifactId>
+ </dependency>
+
+
+ <!-- testing dependencies I'm pretty sure we should trim -->
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>clustering.test</artifactId>
<artifactId>yang-binding</artifactId>
<version>0.6.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-type-provider</artifactId>
+ <version>0.6.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-generator-util</artifactId>
+ <version>${yangtools.binding.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-model-api</artifactId>
+ <version>${yangtools.binding.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-generator-spi</artifactId>
+ <version>${yangtools.binding.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.4</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+ <artifactId>antlr-runtime-osgi</artifactId>
+ <version>4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+ <artifactId>xtend-lib-osgi</artifactId>
+ <version>2.4.2-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-api</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-util</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-common</artifactId>
reference\:file\:../lib/jersey-json-1.17.jar@2:start,\
reference\:file\:../lib/jersey-server-1.17.jar@2:start
+# Netconf startup configuration
+netconf.tcp.address=127.0.0.1
+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.netconf.persist.impl.NoOpStorageAdapter
+
# Set Default start level for framework
osgi.bundles.defaultStartLevel=4
# Extra packages to import from the boot class loader
<!-- OSGi logging bridge -->
<logger name="org.opendaylight.controller.logging.bridge" level="WARN"/>
+ <!-- Netty -->
+ <logger name="io.netty" level="WARN"/>
+
<!-- Openflow Protocol Plugin -->
<logger name="org.opendaylight.controller.protocol_plugin.openflow" level="INFO"/>
<logger name="org.opendaylight.controller.protocol_plugin.openflow.internal.DiscoveryService" level="INFO"/>
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>config-netconf-connector</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+
+ <artifactId>yang-jmx-generator</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+
+ <artifactId>config-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+
+ <artifactId>yang-store-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+
+ <artifactId>yang-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+
+ <artifactId>config-manager</artifactId>
+ <version>${config.version}</version>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+
+ <artifactId>config-manager</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-impl</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+
+ <artifactId>yang-store-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.netconf.confignetconfconnector.osgi.Activator
+ </Bundle-Activator>
+ <Private-Package>
+ org.opendaylight.controller.netconf.confignetconfconnector.mapping.*,
+ org.opendaylight.controller.netconf.confignetconfconnector.operations.*,
+ org.opendaylight.controller.netconf.confignetconfconnector.transactions,
+ org.opendaylight.controller.netconf.confignetconfconnector.util,
+ org.opendaylight.controller.netconf.confignetconfconnector.osgi,
+ org.opendaylight.controller.config.util,
+ </Private-Package>
+ <Import-Package>
+ com.google.common.base,
+ com.google.common.collect,
+ javax.annotation,
+ javax.management,
+ javax.management.openmbean,
+ org.opendaylight.controller.config.api,
+ org.opendaylight.controller.config.api.jmx,
+ org.opendaylight.controller.config.yang.store.api,
+ org.opendaylight.controller.config.yangjmxgenerator,
+ org.opendaylight.controller.config.yangjmxgenerator.attribute,
+ org.opendaylight.controller.netconf.api,
+ org.opendaylight.controller.netconf.mapping.api,
+ org.opendaylight.controller.netconf.util.mapping,
+ org.opendaylight.controller.netconf.util.xml,
+ org.opendaylight.yangtools.yang.common,
+ org.opendaylight.yangtools.yang.model.api,
+ org.osgi.framework,
+ org.osgi.util.tracker,
+ org.slf4j,
+ org.w3c.dom
+ </Import-Package>
+ <Export-Package>
+ </Export-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.confignetconfconnector.mapping.attributes;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+
+public abstract class AttributeIfcSwitchStatement<T> {
+
+ public T switchAttribute(AttributeIfc attributeIfc) {
+
+ if (attributeIfc instanceof JavaAttribute) {
+ return caseJavaAttribute((JavaAttribute) attributeIfc);
+ } else if (attributeIfc instanceof DependencyAttribute) {
+ return caseDependencyAttribute((DependencyAttribute) attributeIfc);
+ } else if (attributeIfc instanceof ListAttribute) {
+ return caseListAttribute((ListAttribute) attributeIfc);
+ } else if (attributeIfc instanceof TOAttribute) {
+ return caseTOAttribute((TOAttribute) attributeIfc);
+ }
+
+ throw new IllegalArgumentException("Unknown attribute type " + attributeIfc.getClass() + ", " + attributeIfc);
+ }
+
+ protected abstract T caseJavaAttribute(JavaAttribute attributeIfc);
+
+ protected abstract T caseDependencyAttribute(DependencyAttribute attributeIfc);
+
+ protected abstract T caseTOAttribute(TOAttribute attributeIfc);
+
+ protected abstract T caseListAttribute(ListAttribute attributeIfc);
+}
--- /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 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 {
+
+ private final A attributeIfc;
+
+ public AbstractAttributeReadingStrategy(A attributeIfc) {
+ this.attributeIfc = attributeIfc;
+ }
+
+ public A getAttributeIfc() {
+ return attributeIfc;
+ }
+
+ @Override
+ public AttributeConfigElement readElement(List<XmlElement> configNodes) {
+ if (configNodes.size() == 0)
+ return AttributeConfigElement.createNullValue(attributeIfc);
+
+ return readElementHook(configNodes);
+ }
+
+ abstract AttributeConfigElement readElementHook(List<XmlElement> configNodes);
+
+}
--- /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.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> {
+
+ private final AttributeReadingStrategy innerStrategy;
+
+ /**
+ * @param attributeIfc
+ * @param innerStrategy
+ */
+ public ArrayAttributeReadingStrategy(AttributeIfc attributeIfc, AttributeReadingStrategy innerStrategy) {
+ super(attributeIfc);
+ this.innerStrategy = innerStrategy;
+ }
+
+ @Override
+ AttributeConfigElement readElementHook(List<XmlElement> configNodes) {
+ List<Object> innerList = Lists.newArrayList();
+ for (int i = 0; i < configNodes.size(); i++) {
+ innerList.add(innerStrategy.readElement(Lists.newArrayList(configNodes.get(i))).getValue());
+ }
+ return AttributeConfigElement.create(getAttributeIfc(), innerList);
+ }
+
+}
--- /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 javax.management.openmbean.OpenType;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy;
+
+import com.google.common.base.Optional;
+
+/**
+ * Parsed xml element containing configuration for one attribute of an instance
+ * of some module. Contains default value extracted from yang file.
+ */
+public class AttributeConfigElement {
+ private final Object dafaultValue;
+ private final Object value;
+
+ private Optional<?> resolvedValue;
+ private Object resolvedDefaultValue;
+ private String jmxName;
+
+ public AttributeConfigElement(Object dafaultValue, Object value) {
+ this.dafaultValue = dafaultValue;
+ this.value = value;
+ }
+
+ public void setJmxName(String jmxName) {
+ this.jmxName = jmxName;
+ }
+
+ public String getJmxName() {
+ return jmxName;
+ }
+
+ public void resolveValue(AttributeResolvingStrategy<?, ? extends OpenType<?>> attributeResolvingStrategy,
+ String attrName) {
+ resolvedValue = attributeResolvingStrategy.parseAttribute(attrName, value);
+ Optional<?> resolvedDefault = attributeResolvingStrategy.parseAttribute(attrName, dafaultValue);
+ resolvedDefaultValue = resolvedDefault.isPresent() ? resolvedDefault.get() : null;
+
+ }
+
+ public static AttributeConfigElement create(AttributeIfc attributeIfc, Object value) {
+ return new AttributeConfigElement(attributeIfc.getNullableDefault(), value);
+ }
+
+ public static AttributeConfigElement createNullValue(AttributeIfc attributeIfc) {
+ return new AttributeConfigElement(attributeIfc.getNullableDefault(), null);
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Optional<?> getResolvedValue() {
+ return resolvedValue;
+ }
+
+ public Object getResolvedDefaultValue() {
+ return resolvedDefaultValue;
+ }
+
+ @Override
+ public String toString() {
+ return "AttributeConfigElement [dafaultValue=" + dafaultValue + ", value=" + 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.fromxml;
+
+import java.util.List;
+
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+
+public interface AttributeReadingStrategy {
+
+ public abstract AttributeConfigElement readElement(List<XmlElement> element);
+
+}
--- /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.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> {
+
+ private final Map<String, AttributeReadingStrategy> innerStrategies;
+
+ public CompositeAttributeReadingStrategy(TOAttribute attributeIfc,
+ Map<String, AttributeReadingStrategy> innerStrategies) {
+ super(attributeIfc);
+ this.innerStrategies = innerStrategies;
+ }
+
+ @Override
+ AttributeConfigElement readElementHook(List<XmlElement> configNodes) {
+
+ Preconditions.checkState(configNodes.size() == 1, "This element should be present only once %s", configNodes);
+
+ XmlElement complexElement = configNodes.get(0);
+
+ Map<String, Object> innerMap = Maps.newHashMap();
+
+ Map<String, AttributeIfc> inner = getAttributeIfc().getYangPropertiesToTypesMap();
+
+ List<XmlElement> recognisedChildren = Lists.newArrayList();
+ for (Entry<String, AttributeIfc> innerAttrEntry : inner.entrySet()) {
+ List<XmlElement> childItem = complexElement.getChildElementsWithSameNamespace(innerAttrEntry.getKey());
+ recognisedChildren.addAll(childItem);
+
+ AttributeConfigElement resolvedInner = innerStrategies.get(innerAttrEntry.getKey()).readElement(childItem);
+
+ innerMap.put(innerAttrEntry.getKey(), resolvedInner.getValue());
+ }
+
+ complexElement.checkUnrecognisedElements(recognisedChildren);
+
+ return AttributeConfigElement.create(getAttributeIfc(), innerMap);
+ }
+
+}
--- /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.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;
+
+public class ObjectNameAttributeReadingStrategy extends AbstractAttributeReadingStrategy<AttributeIfc> {
+
+ public ObjectNameAttributeReadingStrategy(DependencyAttribute attributeIfc) {
+ super(attributeIfc);
+ }
+
+ @Override
+ AttributeConfigElement readElementHook(List<XmlElement> configNodes) {
+
+ XmlElement firstChild = configNodes.get(0);
+ Preconditions.checkState(configNodes.size() == 1, "This element should be present only once " + firstChild
+ + " but was " + configNodes.size());
+
+ Preconditions.checkNotNull(firstChild, "Element %s should be present", firstChild);
+ return AttributeConfigElement.create(getAttributeIfc(), resolve(firstChild));
+ }
+
+ private ObjectNameAttributeMappingStrategy.MappedDependency resolve(XmlElement firstChild) {
+ XmlElement typeElement = firstChild.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
+ String serviceName = typeElement.getTextContent();
+ XmlElement nameElement = firstChild.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
+ String dependencyName = nameElement.getTextContent();
+
+ return new ObjectNameAttributeMappingStrategy.MappedDependency(serviceName, dependencyName);
+ }
+
+}
--- /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 org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.SimpleType;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class ObjectXmlReader extends AttributeIfcSwitchStatement<AttributeReadingStrategy> {
+
+ private String key;
+
+ public Map<String, AttributeReadingStrategy> prepareReading(Map<String, AttributeIfc> yangToAttrConfig) {
+ Map<String, AttributeReadingStrategy> strategies = Maps.newHashMap();
+
+ for (Entry<String, AttributeIfc> attributeEntry : yangToAttrConfig.entrySet()) {
+ AttributeReadingStrategy strat = prepareReadingStrategy(attributeEntry.getKey(), attributeEntry.getValue());
+ strategies.put(attributeEntry.getKey(), strat);
+ }
+ return strategies;
+ }
+
+ private AttributeReadingStrategy prepareReadingStrategy(String key, AttributeIfc attributeIfc) {
+ this.key = key;
+ return switchAttribute(attributeIfc);
+ }
+
+ @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);
+ }
+
+ @Override
+ protected AttributeReadingStrategy caseDependencyAttribute(DependencyAttribute attributeIfc) {
+ return new ObjectNameAttributeReadingStrategy(attributeIfc);
+ }
+
+ @Override
+ protected AttributeReadingStrategy caseTOAttribute(TOAttribute attributeIfc) {
+ Map<String, AttributeIfc> inner = attributeIfc.getYangPropertiesToTypesMap();
+ Map<String, AttributeReadingStrategy> innerStrategies = Maps.newHashMap();
+
+ for (Entry<String, AttributeIfc> innerAttrEntry : inner.entrySet()) {
+ AttributeReadingStrategy innerStrat = prepareReadingStrategy(innerAttrEntry.getKey(),
+ innerAttrEntry.getValue());
+ innerStrategies.put(innerAttrEntry.getKey(), innerStrat);
+ }
+
+ return new CompositeAttributeReadingStrategy(attributeIfc, innerStrategies);
+ }
+
+ @Override
+ protected AttributeReadingStrategy caseListAttribute(ListAttribute attributeIfc) {
+ AttributeIfc innerAttr = attributeIfc.getInnerAttribute();
+ AttributeReadingStrategy innerStrategy = prepareReadingStrategy(key, innerAttr);
+ return new ArrayAttributeReadingStrategy(attributeIfc, innerStrategy);
+ }
+
+}
--- /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.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 SimpleAttributeReadingStrategy(AttributeIfc attributeIfc) {
+ super(attributeIfc);
+ }
+
+ /**
+ * @param elementOpenType
+ */
+ public SimpleAttributeReadingStrategy(OpenType<?> elementOpenType) {
+ super(new AttributeIfcWrapper(elementOpenType));
+ }
+
+ @Override
+ AttributeConfigElement readElementHook(List<XmlElement> configNodes) {
+ XmlElement xmlElement = configNodes.get(0);
+ Preconditions.checkState(configNodes.size() == 1, "This element should be present only once " + xmlElement
+ + " but was " + configNodes.size());
+
+ String textContent = xmlElement.getTextContent();
+
+ Preconditions.checkNotNull(textContent, "This element should contain text %s", xmlElement);
+ return AttributeConfigElement.create(getAttributeIfc(), 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;
+ }
+
+ }
+}
--- /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.mapping;
+
+import javax.management.openmbean.OpenType;
+
+public abstract class AbstractAttributeMappingStrategy<T, O extends OpenType<?>> implements
+ AttributeMappingStrategy<T, O> {
+
+ private final O attrOpenType;
+
+ public AbstractAttributeMappingStrategy(O attributeIfc) {
+ this.attrOpenType = attributeIfc;
+ }
+
+ @Override
+ public O getOpenType() {
+ return attrOpenType;
+ }
+
+}
--- /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.mapping;
+
+import java.lang.reflect.Array;
+import java.util.List;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.OpenType;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+public class ArrayAttributeMappingStrategy extends AbstractAttributeMappingStrategy<List<Object>, ArrayType<?>> {
+
+ private final AttributeMappingStrategy<?, ? extends OpenType<?>> innerElementStrategy;
+
+ public ArrayAttributeMappingStrategy(ArrayType<?> arrayType,
+ AttributeMappingStrategy<?, ? extends OpenType<?>> innerElementStrategy) {
+ super(arrayType);
+ this.innerElementStrategy = innerElementStrategy;
+ }
+
+ @Override
+ public Optional<List<Object>> mapAttribute(Object value) {
+ if (value == null)
+ return Optional.absent();
+
+ Preconditions.checkArgument(value.getClass().isArray(), "Value has to be instanceof Array ");
+
+ List<Object> retVal = Lists.newArrayList();
+
+ for (int i = 0; i < Array.getLength(value); i++) {
+ Object innerValue = Array.get(value, i);
+ // String expectedClassName =
+ // getOpenType().getElementOpenType().getClassName();
+ // String realClassName = value.getClass().getName();
+
+ // Preconditions.checkState(realClassName.contains(expectedClassName),
+ // "Element in collection/array should be of type " +
+ // expectedClassName + " but was "
+ // + realClassName + " for attribute: " + getOpenType());
+
+ Optional<?> mapAttribute = innerElementStrategy.mapAttribute(innerValue);
+
+ if (mapAttribute.isPresent())
+ retVal.add(mapAttribute.get());
+ }
+
+ return Optional.of(retVal);
+ }
+
+}
--- /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.mapping;
+
+import com.google.common.base.Optional;
+
+import javax.management.openmbean.OpenType;
+
+public interface AttributeMappingStrategy<T, O extends OpenType<?>> {
+
+ O getOpenType();
+
+ Optional<T> mapAttribute(Object o);
+
+}
--- /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.mapping;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import java.util.Map;
+import java.util.Set;
+
+public class CompositeAttributeMappingStrategy extends
+ AbstractAttributeMappingStrategy<Map<String, Object>, CompositeType> {
+
+ private final Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies;
+ private final Map<String, String> jmxToJavaNameMapping;
+
+ public CompositeAttributeMappingStrategy(CompositeType compositeType,
+ Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies,
+ Map<String, String> jmxToJavaNameMapping) {
+ super(compositeType);
+ this.innerStrategies = innerStrategies;
+ this.jmxToJavaNameMapping = jmxToJavaNameMapping;
+ }
+
+ @Override
+ public Optional<Map<String, Object>> mapAttribute(Object value) {
+ if (value == null)
+ return Optional.absent();
+
+ Util.checkType(value, CompositeDataSupport.class);
+
+ CompositeDataSupport compositeData = (CompositeDataSupport) value;
+ CompositeType currentType = compositeData.getCompositeType();
+ CompositeType expectedType = getOpenType();
+
+ Set<String> expectedCompositeTypeKeys = expectedType.keySet();
+ Set<String> currentCompositeTypeKeys = currentType.keySet();
+ Preconditions.checkArgument(expectedCompositeTypeKeys.equals(currentCompositeTypeKeys),
+ "Composite type mismatch, expected composite type with attributes " + expectedCompositeTypeKeys
+ + " but was " + currentCompositeTypeKeys);
+
+ Map<String, Object> retVal = Maps.newHashMap();
+
+ for (String jmxName : jmxToJavaNameMapping.keySet()) {
+ String innerAttrJmxName = jmxName;
+ Object innerValue = compositeData.get(innerAttrJmxName);
+
+ AttributeMappingStrategy<?, ? extends OpenType<?>> attributeMappingStrategy = innerStrategies
+ .get(innerAttrJmxName);
+ Optional<?> mapAttribute = attributeMappingStrategy.mapAttribute(innerValue);
+ if (mapAttribute.isPresent())
+ retVal.put(jmxToJavaNameMapping.get(innerAttrJmxName), mapAttribute.get());
+ }
+
+ return Optional.of(retVal);
+ }
+
+}
--- /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.mapping;
+
+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 org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
+
+import com.google.common.collect.Maps;
+
+public class ObjectMapper extends AttributeIfcSwitchStatement<AttributeMappingStrategy<?, ? extends OpenType<?>>> {
+
+ private final Services dependencyTracker;
+
+ public ObjectMapper(Services depTracker) {
+ this.dependencyTracker = depTracker;
+ }
+
+ public Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> prepareMapping(
+ Map<String, AttributeIfc> configDefinition) {
+ Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> strategies = Maps.newHashMap();
+
+ for (Entry<String, AttributeIfc> attrEntry : configDefinition.entrySet()) {
+ strategies.put(attrEntry.getKey(), prepareStrategy(attrEntry.getValue()));
+ }
+
+ return strategies;
+ }
+
+ private AttributeMappingStrategy<?, ? extends OpenType<?>> prepareStrategy(AttributeIfc attributeIfc) {
+ return switchAttribute(attributeIfc);
+ }
+
+ private Map<String, String> createJmxToYangMapping(TOAttribute attributeIfc) {
+ Map<String, String> retVal = Maps.newHashMap();
+ for (Entry<String, AttributeIfc> entry : attributeIfc.getJmxPropertiesToTypesMap().entrySet()) {
+ retVal.put(entry.getKey(), (entry.getValue()).getAttributeYangName());
+ }
+ return retVal;
+ }
+
+ @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);
+ }
+ throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
+ + " or " + ArrayType.class);
+ }
+
+ @Override
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseDependencyAttribute(
+ DependencyAttribute attributeIfc) {
+ String serviceName = attributeIfc.getDependency().getSie().getQName().getLocalName();
+ return new ObjectNameAttributeMappingStrategy((SimpleType<?>) attributeIfc.getOpenType(), dependencyTracker,
+ serviceName);
+ }
+
+ @Override
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseTOAttribute(TOAttribute attributeIfc) {
+ Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies = Maps.newHashMap();
+
+ for (Entry<String, AttributeIfc> innerAttrEntry : attributeIfc.getJmxPropertiesToTypesMap().entrySet()) {
+ innerStrategies.put(innerAttrEntry.getKey(), prepareStrategy(innerAttrEntry.getValue()));
+ }
+
+ return new CompositeAttributeMappingStrategy((CompositeType) attributeIfc.getOpenType(), innerStrategies,
+ createJmxToYangMapping(attributeIfc));
+ }
+
+ @Override
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseListAttribute(ListAttribute attributeIfc) {
+ return new ArrayAttributeMappingStrategy(attributeIfc.getOpenType(),
+ prepareStrategy(attributeIfc.getInnerAttribute()));
+ }
+
+}
--- /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.mapping;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.SimpleType;
+
+public class ObjectNameAttributeMappingStrategy extends
+ AbstractAttributeMappingStrategy<ObjectNameAttributeMappingStrategy.MappedDependency, SimpleType<?>> {
+
+ private final Services tracker;
+ private final String serviceName;
+
+ public ObjectNameAttributeMappingStrategy(SimpleType<?> openType, Services dependencyTracker, String serviceName) {
+ super(openType);
+ this.tracker = dependencyTracker;
+ this.serviceName = serviceName;
+ }
+
+ @Override
+ public Optional<MappedDependency> mapAttribute(Object value) {
+ if (value == null)
+ return Optional.absent();
+
+ String expectedClass = getOpenType().getClassName();
+ String realClass = value.getClass().getName();
+ Preconditions.checkArgument(realClass.equals(expectedClass), "Type mismatch, expected " + expectedClass
+ + " but was " + realClass);
+ Util.checkType(value, ObjectName.class);
+
+ ObjectName on = (ObjectName) value;
+ String refName = tracker.addServiceEntry(serviceName, on);
+
+ return Optional.of(new MappedDependency(serviceName, refName));
+ }
+
+ public static class MappedDependency {
+ private final String serviceName, refName;
+
+ public MappedDependency(String serviceName, String refName) {
+ this.serviceName = serviceName;
+ this.refName = refName;
+ }
+
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ public String getRefName() {
+ return refName;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("MappedDependency{");
+ sb.append("serviceName='").append(serviceName).append('\'');
+ sb.append(", refName='").append(refName).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+}
--- /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.mapping;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+
+import javax.management.openmbean.SimpleType;
+import java.util.Date;
+import java.util.Map;
+
+public class SimpleAttributeMappingStrategy extends AbstractAttributeMappingStrategy<String, SimpleType<?>> {
+
+ public SimpleAttributeMappingStrategy(SimpleType<?> openType) {
+ super(openType);
+ }
+
+ @Override
+ public Optional<String> mapAttribute(Object value) {
+ if (value == null)
+ return Optional.absent();
+
+ String expectedClass = getOpenType().getClassName();
+ String realClass = value.getClass().getName();
+ Preconditions.checkArgument(realClass.equals(expectedClass), "Type mismatch, expected " + expectedClass
+ + " but was " + realClass);
+
+ WriterPlugin prefferedPlugin = writerPlugins.get(value.getClass().getCanonicalName());
+ prefferedPlugin = prefferedPlugin == null ? writerPlugins.get(DEFAULT_WRITER_PLUGIN) : prefferedPlugin;
+ return Optional.of(prefferedPlugin.writeObject(value));
+ }
+
+ private static final String DEFAULT_WRITER_PLUGIN = "default";
+ private static final Map<String, WriterPlugin> writerPlugins = Maps.newHashMap();
+ static {
+ writerPlugins.put(DEFAULT_WRITER_PLUGIN, new DefaultWriterPlugin());
+ writerPlugins.put(Date.class.getCanonicalName(), new DatePlugin());
+ }
+
+ /**
+ * Custom writer plugins must implement this interface.
+ */
+ static interface WriterPlugin {
+ String writeObject(Object value);
+ }
+
+ static class DefaultWriterPlugin implements WriterPlugin {
+
+ @Override
+ public String writeObject(Object value) {
+ return value.toString();
+ }
+ }
+
+ static class DatePlugin implements WriterPlugin {
+
+ @Override
+ public String writeObject(Object value) {
+ Preconditions.checkArgument(value instanceof Date, "Attribute must be Date");
+ return Util.writeDate((Date) 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.resolving;
+
+import javax.management.openmbean.OpenType;
+
+abstract class AbstractAttributeResolvingStrategy<T, O extends OpenType<?>> implements AttributeResolvingStrategy<T, O> {
+ private final O openType;
+
+ public AbstractAttributeResolvingStrategy(O openType) {
+ this.openType = openType;
+ }
+
+ @Override
+ public O getOpenType() {
+ return openType;
+ }
+}
--- /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.resolving;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import java.lang.reflect.Array;
+import java.util.List;
+
+final class ArrayAttributeResolvingStrategy extends AbstractAttributeResolvingStrategy<Object, ArrayType<?>> {
+
+ private final AttributeResolvingStrategy<?, ? extends OpenType<?>> innerTypeResolvingStrategy;
+
+ private static final Logger logger = LoggerFactory.getLogger(ArrayAttributeResolvingStrategy.class);
+
+ public ArrayAttributeResolvingStrategy(AttributeResolvingStrategy<?, ? extends OpenType<?>> innerTypeResolved,
+ ArrayType<?> openType) {
+ super(openType);
+ this.innerTypeResolvingStrategy = innerTypeResolved;
+ }
+
+ @Override
+ public Optional<Object> parseAttribute(String attrName, Object value) {
+ if (value == null) {
+ return Optional.absent();
+ }
+
+ Util.checkType(value, List.class);
+ List<?> valueList = (List<?>) value;
+
+ Class<?> innerTypeClass = null;
+
+ if (innerTypeResolvingStrategy.getOpenType() instanceof CompositeType) {
+ innerTypeClass = CompositeDataSupport.class;
+ } else
+ try {
+ innerTypeClass = Class.forName(getOpenType().getElementOpenType().getClassName());
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Unable to locate class for "
+ + getOpenType().getElementOpenType().getClassName(), e);
+ }
+
+ Object parsedArray = null;
+
+ if (getOpenType().isPrimitiveArray()) {
+ Class<?> primitiveType = getPrimitiveType(innerTypeClass);
+ parsedArray = Array.newInstance(primitiveType, valueList.size());
+ } else
+ parsedArray = Array.newInstance(innerTypeClass, valueList.size());
+
+ int i = 0;
+ for (Object innerValue : valueList) {
+ Optional<?> parsedElement = innerTypeResolvingStrategy.parseAttribute(attrName + "_" + i, innerValue);
+
+ if (!parsedElement.isPresent())
+ continue;
+
+ Array.set(parsedArray, i, parsedElement.get());
+ // parsedArray[i] = parsedElement.get();
+ i++;
+ }
+
+ logger.debug("Attribute {} : {} parsed to type {} as {}", attrName, value, getOpenType(),
+ toStringArray(parsedArray));
+
+ return Optional.of(parsedArray);
+ }
+
+ private static String toStringArray(Object array) {
+ StringBuilder build = new StringBuilder(array.toString());
+ build.append(" [");
+ for (int i = 0; i < Array.getLength(array); i++) {
+ build.append(Array.get(array, i).toString());
+ build.append(",");
+ }
+ build.append("]]");
+ return build.toString();
+ }
+
+ private static Class<?> getPrimitiveType(Class<?> innerTypeClass) {
+ try {
+ return (Class<?>) innerTypeClass.getField("TYPE").get(null);
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to determine primitive type to " + innerTypeClass);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ResolvedArrayTypeAttributeType [innerTypeResolved=" + innerTypeResolvingStrategy + "]";
+ }
+}
--- /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.resolving;
+
+import com.google.common.base.Optional;
+
+import javax.management.openmbean.OpenType;
+
+/**
+ * Create real object from String or Map that corresponds to given opentype.
+ */
+public interface AttributeResolvingStrategy<T, O extends OpenType<?>> {
+ O getOpenType();
+
+ Optional<T> parseAttribute(String attrName, Object 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.resolving;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import java.util.Map;
+
+final class CompositeAttributeResolvingStrategy extends
+ AbstractAttributeResolvingStrategy<CompositeDataSupport, CompositeType> {
+ private final Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerTypes;
+ private final Map<String, String> yangToJavaAttrMapping;
+
+ private static final Logger logger = LoggerFactory.getLogger(CompositeAttributeResolvingStrategy.class);
+
+ CompositeAttributeResolvingStrategy(Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerTypes,
+ CompositeType openType, Map<String, String> yangToJavaAttrMapping) {
+ super(openType);
+ this.innerTypes = innerTypes;
+ this.yangToJavaAttrMapping = yangToJavaAttrMapping;
+ }
+
+ @Override
+ public String toString() {
+ return "ResolvedCompositeAttribute [" + innerTypes + "]";
+ }
+
+ @Override
+ public Optional<CompositeDataSupport> parseAttribute(String attrName, Object value) {
+
+ if (value == null) {
+ return Optional.absent();
+ }
+
+ Util.checkType(value, Map.class);
+ Map<?, ?> valueMap = (Map<?, ?>) value;
+
+ Map<String, Object> items = Maps.newHashMap();
+ Map<String, OpenType<?>> openTypes = Maps.newHashMap();
+
+ for (Object innerAttrName : innerTypes.keySet()) {
+ Preconditions.checkState(innerAttrName instanceof String, "Attribute name must be string");
+ String innerAttrNameStr = (String) innerAttrName;
+
+ AttributeResolvingStrategy<?, ? extends OpenType<?>> attributeResolvingStrategy = innerTypes
+ .get(innerAttrName);
+
+ Object valueToParse = valueMap.get(innerAttrName);
+
+ Optional<?> parsedInnerValue = attributeResolvingStrategy.parseAttribute(innerAttrNameStr, valueToParse);
+
+ openTypes.put(innerAttrNameStr, attributeResolvingStrategy.getOpenType());
+
+ items.put(yangToJavaAttrMapping.get(innerAttrNameStr),
+ parsedInnerValue.isPresent() ? parsedInnerValue.get() : null);
+ }
+
+ CompositeDataSupport parsedValue = null;
+ try {
+ parsedValue = new CompositeDataSupport(getOpenType(), items);
+ } catch (OpenDataException e) {
+ throw new RuntimeException("An error occured during restoration of composite type " + this
+ + " for attribute " + attrName + " from value " + value, e);
+ }
+
+ logger.debug("Attribute {} : {} parsed to type {} as {}", attrName, value, getOpenType(), parsedValue);
+
+ return Optional.of(parsedValue);
+ }
+}
--- /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.resolving;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services.ServiceInstance;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.SimpleType;
+
+public class ObjectNameAttributeResolvingStrategy extends AbstractAttributeResolvingStrategy<ObjectName, SimpleType<?>> {
+
+ private final Services serviceTracker;
+ private static final Logger logger = LoggerFactory.getLogger(ObjectNameAttributeResolvingStrategy.class);
+
+ ObjectNameAttributeResolvingStrategy(Services serviceTracker) {
+ super(SimpleType.OBJECTNAME);
+ this.serviceTracker = serviceTracker;
+ }
+
+ @Override
+ public Optional<ObjectName> parseAttribute(String attrName, Object value) {
+ if (value == null) {
+ return Optional.absent();
+ }
+
+ Util.checkType(value, ObjectNameAttributeMappingStrategy.MappedDependency.class);
+
+ ObjectNameAttributeMappingStrategy.MappedDependency mappedDep = (ObjectNameAttributeMappingStrategy.MappedDependency) value;
+ ServiceInstance byRefName = serviceTracker.getByServiceAndRefName(mappedDep.getServiceName(),
+ mappedDep.getRefName());
+ ObjectName on = ObjectNameUtil.createReadOnlyModuleON(byRefName.getModuleName(), byRefName.getInstanceName());
+ logger.debug("Attribute {} : {} parsed to type {}", attrName, value, getOpenType());
+ return Optional.of(on);
+ }
+
+}
--- /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.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 org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+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;
+
+public class ObjectResolver extends AttributeIfcSwitchStatement<AttributeResolvingStrategy<?, ? extends OpenType<?>>> {
+
+ private final Services serviceTracker;
+ private OpenType<?> openType;
+
+ public ObjectResolver(Services serviceTracker) {
+ this.serviceTracker = serviceTracker;
+ }
+
+ public Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> prepareResolving(
+ Map<String, AttributeIfc> configDefinition) {
+ Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> strategies = Maps.newHashMap();
+
+ for (Entry<String, AttributeIfc> attrEntry : configDefinition.entrySet()) {
+ strategies.put(attrEntry.getKey(),
+ prepareStrategy(attrEntry.getValue(), attrEntry.getValue().getOpenType()));
+ }
+
+ return strategies;
+ }
+
+ private AttributeResolvingStrategy<?, ? extends OpenType<?>> prepareStrategy(AttributeIfc attributeIfc,
+ OpenType<?> openType) {
+
+ this.openType = openType;
+ return switchAttribute(attributeIfc);
+ }
+
+ private Map<String, String> createYangToJmxMapping(TOAttribute attributeIfc) {
+ Map<String, String> retVal = Maps.newHashMap();
+ for (Entry<String, AttributeIfc> entry : attributeIfc.getYangPropertiesToTypesMap().entrySet()) {
+ retVal.put(entry.getKey(), (entry.getValue()).getLowerCaseCammelCase());
+ }
+ return retVal;
+ }
+
+ @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);
+ }
+ throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
+ + " or " + ArrayType.class);
+ }
+
+ @Override
+ protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseDependencyAttribute(
+ DependencyAttribute attributeIfc) {
+ return new ObjectNameAttributeResolvingStrategy(serviceTracker);
+ }
+
+ @Override
+ protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseTOAttribute(TOAttribute attributeIfc) {
+ CompositeType compositeType = (CompositeType) openType;
+ 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);
+ innerMap.put(innerAttributeIfc.getAttributeYangName(),
+ prepareStrategy(innerAttributeIfc, compositeType.getType(innerName)));
+ }
+ return new CompositeAttributeResolvingStrategy(innerMap, compositeType, createYangToJmxMapping(attributeIfc));
+ }
+
+ @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);
+ }
+
+}
--- /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.resolving;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.openmbean.SimpleType;
+import java.lang.reflect.Method;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Map;
+
+final class SimpleAttributeResolvingStrategy extends AbstractAttributeResolvingStrategy<Object, SimpleType<?>> {
+
+ private static final Logger logger = LoggerFactory.getLogger(SimpleAttributeResolvingStrategy.class);
+
+ SimpleAttributeResolvingStrategy(SimpleType<?> simpleType) {
+ super(simpleType);
+ }
+
+ @Override
+ public String toString() {
+ return "ResolvedSimpleAttribute [" + getOpenType().getClassName() + "]";
+ }
+
+ @Override
+ public Optional<Object> parseAttribute(String attrName, Object value) {
+ if (value == null) {
+ return Optional.absent();
+ }
+
+ Class<?> cls;
+ try {
+ cls = Class.forName(getOpenType().getClassName());
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Unable to locate class for " + getOpenType().getClassName(), e);
+ }
+
+ Util.checkType(value, String.class);
+
+ Resolver prefferedPlugin = resolverPlugins.get(cls.getCanonicalName());
+ prefferedPlugin = prefferedPlugin == null ? resolverPlugins.get(DEFAULT_RESOLVERS) : prefferedPlugin;
+
+ Object parsedValue = prefferedPlugin.resolveObject(cls, attrName, (String) value);
+ logger.debug("Attribute {} : {} parsed to type {} with value {}", attrName, value, getOpenType(), parsedValue);
+ return Optional.of(parsedValue);
+ }
+
+ private static final String DEFAULT_RESOLVERS = "default";
+ private static final Map<String, Resolver> resolverPlugins = Maps.newHashMap();
+
+ static {
+ resolverPlugins.put(DEFAULT_RESOLVERS, new DefaultResolver());
+ resolverPlugins.put(String.class.getCanonicalName(), new StringResolver());
+ resolverPlugins.put(Date.class.getCanonicalName(), new DateResolver());
+ resolverPlugins.put(Character.class.getCanonicalName(), new CharResolver());
+ resolverPlugins.put(BigInteger.class.getCanonicalName(), new BigIntegerResolver());
+ }
+
+ static interface Resolver {
+ Object resolveObject(Class<?> type, String attrName, String value);
+ }
+
+ static class DefaultResolver implements Resolver {
+
+ @Override
+ public Object resolveObject(Class<?> type, String attrName, String value) {
+ try {
+ Object parsedValue = parseObject(type, value);
+ return parsedValue;
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to resolve attribute " + attrName + " from " + value, e);
+ }
+ }
+
+ protected Object parseObject(Class<?> type, String value) throws Exception {
+ Method method = type.getMethod("valueOf", String.class);
+ Object parsedValue = method.invoke(null, value);
+ return parsedValue;
+ }
+ }
+
+ static class StringResolver extends DefaultResolver {
+
+ @Override
+ protected Object parseObject(Class<?> type, String value) throws Exception {
+ return value;
+ }
+ }
+
+ static class BigIntegerResolver extends DefaultResolver {
+
+ @Override
+ protected Object parseObject(Class<?> type, String value) throws Exception {
+ return new BigInteger(value);
+ }
+ }
+
+ static class CharResolver extends DefaultResolver {
+
+ @Override
+ protected Object parseObject(Class<?> type, String value) throws Exception {
+ return new Character(value.charAt(0));
+ }
+ }
+
+ static class DateResolver extends DefaultResolver {
+
+ @Override
+ protected Object parseObject(Class<?> type, String value) throws Exception {
+ return Util.readDate(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 java.util.List;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.w3c.dom.Element;
+
+public class ArrayAttributeWritingStrategy implements AttributeWritingStrategy {
+
+ private final AttributeWritingStrategy innnerStrategy;
+
+ public ArrayAttributeWritingStrategy(AttributeWritingStrategy innerStrategy) {
+ this.innnerStrategy = innerStrategy;
+ }
+
+ @Override
+ public void writeElement(Element parentElement, String namespace, Object value) {
+ Util.checkType(value, List.class);
+
+ for (Object innerObject : ((List<?>) value)) {
+ innnerStrategy.writeElement(parentElement, namespace, innerObject);
+ }
+
+ }
+
+}
--- /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 org.w3c.dom.Element;
+
+public interface AttributeWritingStrategy {
+
+ void writeElement(Element parentElement, String namespace, Object 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 java.util.Map;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class CompositeAttributeWritingStrategy implements AttributeWritingStrategy {
+
+ protected final String key;
+ protected final Document document;
+ protected final Map<String, AttributeWritingStrategy> innerStrats;
+
+ public CompositeAttributeWritingStrategy(Document document, String key,
+ Map<String, AttributeWritingStrategy> innerStrats) {
+ this.document = document;
+ this.key = key;
+ this.innerStrats = innerStrats;
+ }
+
+ @Override
+ public void writeElement(Element parentElement, String namespace, Object value) {
+ Util.checkType(value, Map.class);
+
+ Element innerNode = document.createElement(key);
+ XmlUtil.addNamespaceAttr(innerNode, namespace);
+
+ Map<?, ?> map = (Map<?, ?>) value;
+
+ for (Entry<?, ?> innerObjectEntry : map.entrySet()) {
+ Util.checkType(innerObjectEntry.getKey(), String.class);
+
+ String innerKey = (String) innerObjectEntry.getKey();
+ Object innerValue = innerObjectEntry.getValue();
+
+ innerStrats.get(innerKey).writeElement(innerNode, namespace, innerValue);
+ }
+ parentElement.appendChild(innerNode);
+ }
+}
--- /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 org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+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;
+
+public class ObjectNameAttributeWritingStrategy implements AttributeWritingStrategy {
+
+ private final Document document;
+ private final String key;
+
+ /**
+ * @param document
+ * @param key
+ */
+ public ObjectNameAttributeWritingStrategy(Document document, String key) {
+ this.document = document;
+ this.key = key;
+ }
+
+ @Override
+ public void writeElement(Element parentElement, String namespace, Object value) {
+ Util.checkType(value, ObjectNameAttributeMappingStrategy.MappedDependency.class);
+ Element innerNode = document.createElement(key);
+ XmlUtil.addNamespaceAttr(innerNode, namespace);
+
+ String moduleName = ((ObjectNameAttributeMappingStrategy.MappedDependency) value).getServiceName();
+ String refName = ((ObjectNameAttributeMappingStrategy.MappedDependency) value).getRefName();
+
+ final Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY, moduleName);
+ innerNode.appendChild(typeElement);
+
+ final Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, refName);
+ innerNode.appendChild(nameElement);
+
+ parentElement.appendChild(innerNode);
+ }
+
+}
--- /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 com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
+import org.w3c.dom.Document;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.SimpleType;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class ObjectXmlWriter extends AttributeIfcSwitchStatement<AttributeWritingStrategy> {
+
+ private Document document;
+ private String key;
+
+ public Map<String, AttributeWritingStrategy> prepareWriting(Map<String, AttributeIfc> yangToAttrConfig,
+ Document document) {
+
+ Map<String, AttributeWritingStrategy> preparedWriting = Maps.newHashMap();
+
+ for (Entry<String, AttributeIfc> mappedAttributeEntry : yangToAttrConfig.entrySet()) {
+ String key = mappedAttributeEntry.getKey();
+ AttributeIfc value = mappedAttributeEntry.getValue();
+ AttributeWritingStrategy strat = prepareWritingStrategy(key, value, document);
+ preparedWriting.put(key, strat);
+ }
+
+ return preparedWriting;
+ }
+
+ private AttributeWritingStrategy prepareWritingStrategy(String key, AttributeIfc expectedAttr, Document document) {
+ Preconditions.checkNotNull(expectedAttr, "Mbean attributes mismatch, unable to find expected attribute for %s",
+ key);
+ this.document = document;
+ this.key = key;
+ return switchAttribute(expectedAttr);
+ }
+
+ @Override
+ protected AttributeWritingStrategy caseJavaAttribute(JavaAttribute attributeIfc) {
+
+ 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 caseDependencyAttribute(DependencyAttribute attributeIfc) {
+ return new ObjectNameAttributeWritingStrategy(document, key);
+ }
+
+ @Override
+ protected AttributeWritingStrategy caseTOAttribute(TOAttribute attributeIfc) {
+ Map<String, AttributeWritingStrategy> innerStrats = Maps.newHashMap();
+ String currentKey = key;
+ for (Entry<String, AttributeIfc> innerAttrEntry : attributeIfc.getYangPropertiesToTypesMap().entrySet()) {
+
+ AttributeWritingStrategy innerStrategy = prepareWritingStrategy(innerAttrEntry.getKey(),
+ innerAttrEntry.getValue(), document);
+ innerStrats.put(innerAttrEntry.getKey(), innerStrategy);
+ }
+
+ return new CompositeAttributeWritingStrategy(document, currentKey, innerStrats);
+ }
+
+ @Override
+ protected AttributeWritingStrategy caseListAttribute(ListAttribute attributeIfc) {
+ AttributeIfc inner = attributeIfc.getInnerAttribute();
+ AttributeWritingStrategy innerStrategy = prepareWritingStrategy(key, inner, document);
+ return new ArrayAttributeWritingStrategy(innerStrategy);
+ }
+
+}
--- /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 java.util.Map;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class RuntimeBeanEntryWritingStrategy extends CompositeAttributeWritingStrategy {
+
+ public RuntimeBeanEntryWritingStrategy(Document document, String key,
+ Map<String, AttributeWritingStrategy> innerStrats) {
+ super(document, key, innerStrats);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.opendaylight.controller.config.netconf.mapping.attributes.toxml.
+ * AttributeWritingStrategy#writeElement(org.w3c.dom.Element,
+ * java.lang.Object)
+ */
+ @Override
+ public void writeElement(Element parentElement, String namespace, Object value) {
+ Util.checkType(value, Map.class);
+
+ Element innerNode = document.createElement(key);
+
+ Map<?, ?> map = (Map<?, ?>) value;
+
+ for (Entry<?, ?> runtimeBeanInstanceMappingEntry : map.entrySet()) {
+
+ // wrap runtime attributes with number assigned to current runtime
+ // bean
+ Util.checkType(runtimeBeanInstanceMappingEntry.getValue(), Map.class);
+ Map<?, ?> innerMap = (Map<?, ?>) runtimeBeanInstanceMappingEntry.getValue();
+ Element runtimeInstanceNode = document.createElement("_"
+ + (String) runtimeBeanInstanceMappingEntry.getKey());
+ innerNode.appendChild(runtimeInstanceNode);
+
+ for (Entry<?, ?> innerObjectEntry : innerMap.entrySet()) {
+
+ Util.checkType(innerObjectEntry.getKey(), String.class);
+
+ String innerKey = (String) innerObjectEntry.getKey();
+ Object innerValue = innerObjectEntry.getValue();
+
+ innerStrats.get(innerKey).writeElement(runtimeInstanceNode, namespace, innerValue);
+ }
+ }
+ parentElement.appendChild(innerNode);
+
+ }
+
+}
--- /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 org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class SimpleAttributeWritingStrategy implements AttributeWritingStrategy {
+
+ private final Document document;
+ private final String key;
+
+ /**
+ * @param document
+ * @param key
+ */
+ public SimpleAttributeWritingStrategy(Document document, String key) {
+ this.document = document;
+ this.key = key;
+ }
+
+ @Override
+ public void writeElement(Element parentElement, String namespace, Object value) {
+ Util.checkType(value, String.class);
+ Element innerNode = XmlUtil.createTextElement(document, key, (String) value);
+ XmlUtil.addNamespaceAttr(innerNode, namespace);
+ parentElement.appendChild(innerNode);
+ }
+
+}
--- /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.config;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
+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.util.xml.XmlElement;
+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.*;
+import java.util.Map.Entry;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+
+public class Config {
+
+ private final Map<String, Map<String, ModuleConfig>> moduleConfigs;
+
+ public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs) {
+ this.moduleConfigs = moduleConfigs;
+ }
+
+ private Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
+ Services serviceTracker) {
+ Multimap<String, ObjectName> moduleToInstances = mapInstancesToModules(instancesToMap);
+
+ Map<String, Map<String, Collection<ObjectName>>> retVal = Maps.newLinkedHashMap();
+
+ for (String namespace : moduleConfigs.keySet()) {
+
+ Map<String, Collection<ObjectName>> innerRetVal = Maps.newHashMap();
+
+ for (Entry<String, ModuleConfig> mbeEntry : moduleConfigs.get(namespace).entrySet()) {
+
+ String moduleName = mbeEntry.getKey();
+ Collection<ObjectName> instances = moduleToInstances.get(moduleName);
+
+ if (instances == null)
+ continue;
+
+ innerRetVal.put(moduleName, instances);
+
+ // All found instances add to service tracker in advance
+ // This way all instances will be serialized as all available
+ // services when get-config is triggered
+ // (even if they are not used as services by other onstances)
+ // = more user friendly
+ addServices(serviceTracker, instances, mbeEntry.getValue().getProvidedServices());
+
+ }
+
+ retVal.put(namespace, innerRetVal);
+ }
+ return retVal;
+ }
+
+ private void addServices(Services serviceTracker, Collection<ObjectName> instances,
+ Collection<String> providedServices) {
+ for (ObjectName instanceOn : instances) {
+ for (String serviceName : providedServices) {
+ serviceTracker.addServiceEntry(serviceName, instanceOn);
+ }
+ }
+ }
+
+ private static Multimap<String, ObjectName> mapInstancesToModules(Set<ObjectName> instancesToMap) {
+ Multimap<String, ObjectName> retVal = HashMultimap.create();
+
+ for (ObjectName objectName : instancesToMap) {
+ String factoryName = ObjectNameUtil.getFactoryName(objectName);
+ retVal.put(factoryName, objectName);
+ }
+ return retVal;
+ }
+
+ // public Element toXml(Set<ObjectName> instancesToMap, String namespace,
+ // Document document) {
+ // return toXml(instancesToMap, Optional.of(namespace), document);
+ // }
+
+ public Element toXml(Set<ObjectName> instancesToMap, Optional<String> maybeNamespace, Document document,
+ Element dataElement) {
+ Services serviceTracker = new Services();
+
+ Map<String, Map<String, Collection<ObjectName>>> moduleToInstances = getMappedInstances(instancesToMap,
+ serviceTracker);
+
+ Element root = dataElement;
+ if (maybeNamespace.isPresent()) {
+ XmlUtil.addNamespaceAttr(root, maybeNamespace.get());
+ }
+
+ Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY);
+ XmlUtil.addNamespaceAttr(modulesElement,
+ XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+ root.appendChild(modulesElement);
+ for (String moduleNamespace : moduleToInstances.keySet()) {
+ for (Entry<String, Collection<ObjectName>> moduleMappingEntry : moduleToInstances.get(moduleNamespace)
+ .entrySet()) {
+
+ ModuleConfig mapping = moduleConfigs.get(moduleNamespace).get(moduleMappingEntry.getKey());
+
+ if (moduleMappingEntry.getValue().isEmpty()) {
+ addEmptyModulesCommented(document, modulesElement, moduleNamespace, moduleMappingEntry);
+ } else {
+ for (ObjectName objectName : moduleMappingEntry.getValue()) {
+ modulesElement
+ .appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace));
+ }
+ }
+
+ }
+ }
+
+ root.appendChild(serviceTracker.toXml(serviceTracker.getMappedServices(), document));
+
+ return root;
+ }
+
+ private void addEmptyModulesCommented(Document document, Element root, String moduleNamespace,
+ Entry<String, Collection<ObjectName>> moduleMappingEntry) {
+ Element emptyModule = document.createElement(XmlNetconfConstants.MODULE_KEY);
+
+ Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY,
+ moduleMappingEntry.getKey());
+ emptyModule.appendChild(typeElement);
+
+ root.appendChild(document.createComment(XmlUtil.toString(emptyModule, false)));
+ }
+
+ // TODO refactor, replace string representing namespace with namespace class
+ // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
+ // class
+ public Map<String, Multimap<String, ModuleElementResolved>> fromXml(XmlElement xml) {
+ Map<String, Multimap<String, ModuleElementResolved>> retVal = Maps.newHashMap();
+
+ List<XmlElement> recognisedChildren = Lists.newArrayList();
+
+ Services serviceTracker = fromXmlServices(xml, recognisedChildren);
+ List<XmlElement> moduleElements = fromXmlModules(xml, recognisedChildren);
+
+ xml.checkUnrecognisedElements(recognisedChildren);
+
+ for (XmlElement moduleElement : moduleElements) {
+ resolveModule(retVal, serviceTracker, moduleElement);
+ }
+
+ return retVal;
+ }
+
+ private List<XmlElement> fromXmlModules(XmlElement xml, List<XmlElement> recognisedChildren) {
+ Optional<XmlElement> modulesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY,
+ XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+ List<XmlElement> moduleElements;
+ if (modulesElement.isPresent()) {
+ moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlNetconfConstants.MODULE_KEY);
+ recognisedChildren.add(modulesElement.get());
+ modulesElement.get().checkUnrecognisedElements(moduleElements);
+ } else {
+ moduleElements = Lists.newArrayList();
+ }
+ return moduleElements;
+ }
+
+ private void resolveModule(Map<String, Multimap<String, ModuleElementResolved>> retVal, Services serviceTracker,
+ XmlElement moduleElement) {
+ XmlElement typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
+ Entry<String, String> prefixToNamespace = typeElement.findNamespaceOfTextContent();
+ String moduleNamespace = prefixToNamespace.getValue();
+ XmlElement nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
+ String instanceName = nameElement.getTextContent();
+ String factoryNameWithPrefix = typeElement.getTextContent();
+ String prefixOrEmptyString = prefixToNamespace.getKey();
+ String factoryName = getFactoryName(factoryNameWithPrefix, prefixOrEmptyString);
+
+ ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName);
+
+ Multimap<String, ModuleElementResolved> innerMap = retVal.get(moduleNamespace);
+ if (innerMap == null) {
+ innerMap = HashMultimap.create();
+ retVal.put(moduleNamespace, innerMap);
+ }
+
+ ModuleElementResolved moduleElementResolved = moduleMapping.fromXml(moduleElement, serviceTracker,
+ instanceName, moduleNamespace);
+
+ innerMap.put(factoryName, moduleElementResolved);
+ }
+
+ private Services fromXmlServices(XmlElement xml, List<XmlElement> recognisedChildren) {
+ Optional<XmlElement> servicesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY,
+ XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+
+ Map<String, Map<String, String>> mappedServices;
+ if (servicesElement.isPresent()) {
+ mappedServices = Services.fromXml(servicesElement.get());
+ recognisedChildren.add(servicesElement.get());
+ } else {
+ mappedServices = new HashMap<>();
+ }
+
+ return Services.resolveServices(mappedServices);
+ }
+
+ private String getFactoryName(String factoryNameWithPrefix, String prefixOrEmptyString) {
+ checkState(
+ factoryNameWithPrefix.startsWith(prefixOrEmptyString),
+ format("Internal error: text " + "content '%s' of type node does not start with prefix '%s'",
+ factoryNameWithPrefix, prefixOrEmptyString));
+
+ int factoryNameAfterPrefixIndex;
+ if (prefixOrEmptyString.isEmpty()) {
+ factoryNameAfterPrefixIndex = 0;
+ } else {
+ factoryNameAfterPrefixIndex = prefixOrEmptyString.length() + 1;
+ }
+ return factoryNameWithPrefix.substring(factoryNameAfterPrefixIndex);
+ }
+
+ private ModuleConfig getModuleMapping(String moduleNamespace, String instanceName, String factoryName) {
+ Map<String, ModuleConfig> mappingsFromNamespace = moduleConfigs.get(moduleNamespace);
+
+ Preconditions.checkNotNull(mappingsFromNamespace,
+ "Namespace %s, defined in: module %s of type %s not found, available namespaces: %s", moduleNamespace,
+ instanceName, factoryName, moduleConfigs.keySet());
+
+ ModuleConfig moduleMapping = mappingsFromNamespace.get(factoryName);
+ checkState(moduleMapping != null, "Cannot find mapping for module type " + factoryName);
+ return moduleMapping;
+ }
+
+}
--- /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.config;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeReadingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.ObjectXmlReader;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.AttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectMapper;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.ObjectResolver;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml.AttributeWritingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml.ObjectXmlWriter;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.OpenType;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public final class InstanceConfig {
+ private static final Logger logger = LoggerFactory.getLogger(InstanceConfig.class);
+
+ private final Map<String, AttributeIfc> yangToAttrConfig;
+ private final Map<String, AttributeIfc> jmxToAttrConfig;
+ private final ConfigRegistryClient configRegistryClient;
+
+ public InstanceConfig(ConfigRegistryClient configRegistryClient, Map<String, AttributeIfc> yangNamesToAttributes) {
+ this.yangToAttrConfig = yangNamesToAttributes;
+ this.jmxToAttrConfig = reverseMap(yangNamesToAttributes);
+ this.configRegistryClient = configRegistryClient;
+ }
+
+ private Map<String, Object> getMappedConfiguration(ObjectName on, Services depTracker) {
+
+ // TODO make field, mappingStrategies can be instantiated only once
+ Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> mappingStrategies = new ObjectMapper(depTracker)
+ .prepareMapping(jmxToAttrConfig);
+
+ Map<String, Object> toXml = Maps.newHashMap();
+
+ for (Entry<String, AttributeIfc> configDefEntry : jmxToAttrConfig.entrySet()) {
+
+ // Skip children runtime beans as they are mapped by InstanceRuntime
+ if (configDefEntry.getValue() instanceof RuntimeBeanEntry)
+ continue;
+
+ Object value = configRegistryClient.getAttributeCurrentValue(on, configDefEntry.getKey());
+ try {
+ AttributeMappingStrategy<?, ? extends OpenType<?>> attributeMappingStrategy = mappingStrategies
+ .get(configDefEntry.getKey());
+ Optional<?> a = attributeMappingStrategy.mapAttribute(value);
+ if (a.isPresent() == false)
+ continue;
+
+ toXml.put(configDefEntry.getValue().getAttributeYangName(), a.get());
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to map value " + value + " to attribute "
+ + configDefEntry.getKey(), e);
+ }
+ }
+
+ return toXml;
+ }
+
+ public Element toXml(ObjectName on, Services depTracker, String namespace, Document document, Element rootElement) {
+
+ Element cfgElement = rootElement;
+
+ Map<String, AttributeWritingStrategy> strats = new ObjectXmlWriter().prepareWriting(yangToAttrConfig, document);
+
+ Map<String, Object> mappedConfig = getMappedConfiguration(on, depTracker);
+
+ for (Entry<String, ?> mappingEntry : mappedConfig.entrySet()) {
+ try {
+ strats.get(mappingEntry.getKey()).writeElement(cfgElement, namespace, mappingEntry.getValue());
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to write value " + mappingEntry.getValue() + " for attribute "
+ + mappingEntry.getValue(), e);
+ }
+ }
+
+ return cfgElement;
+ }
+
+ private void resolveConfiguration(InstanceConfigElementResolved mappedConfig, Services depTracker) {
+
+ // TODO make field, resolvingStrategies can be instantiated only once
+ Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> resolvingStrategies = new ObjectResolver(
+ depTracker).prepareResolving(yangToAttrConfig);
+
+ for (Entry<String, AttributeConfigElement> configDefEntry : mappedConfig.getConfiguration().entrySet()) {
+ try {
+
+ AttributeResolvingStrategy<?, ? extends OpenType<?>> attributeResolvingStrategy = resolvingStrategies
+ .get(configDefEntry.getKey());
+
+ configDefEntry.getValue().resolveValue(attributeResolvingStrategy, configDefEntry.getKey());
+ configDefEntry.getValue().setJmxName(
+ yangToAttrConfig.get(configDefEntry.getKey()).getUpperCaseCammelCase());
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to resolve value " + configDefEntry.getValue()
+ + " to attribute " + configDefEntry.getKey(), e);
+ }
+ }
+ }
+
+ public InstanceConfigElementResolved fromXml(XmlElement moduleElement, Services services, String moduleNamespace) {
+ Map<String, AttributeConfigElement> retVal = Maps.newHashMap();
+
+ Map<String, AttributeReadingStrategy> strats = new ObjectXmlReader().prepareReading(yangToAttrConfig);
+ List<XmlElement> recognisedChildren = Lists.newArrayList();
+
+ XmlElement type = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
+ XmlElement name = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
+ List<XmlElement> typeAndName = Lists.newArrayList(type, name);
+
+ for (Entry<String, AttributeReadingStrategy> readStratEntry : strats.entrySet()) {
+ List<XmlElement> configNodes = getConfigNodes(moduleElement, moduleNamespace, readStratEntry.getKey(),
+ recognisedChildren, typeAndName);
+ AttributeConfigElement readElement = readStratEntry.getValue().readElement(configNodes);
+ retVal.put(readStratEntry.getKey(), readElement);
+ }
+
+ recognisedChildren.addAll(typeAndName);
+ moduleElement.checkUnrecognisedElements(recognisedChildren);
+
+ // TODO: add check for conflicts between global and local edit strategy
+ String perInstanceEditStrategy = moduleElement.getAttribute(XmlNetconfConstants.OPERATION_ATTR_KEY,
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+ InstanceConfigElementResolved instanceConfigElementResolved = perInstanceEditStrategy.equals("") ? new InstanceConfigElementResolved(
+ retVal) : new InstanceConfigElementResolved(perInstanceEditStrategy, retVal);
+
+ resolveConfiguration(instanceConfigElementResolved, services);
+ return instanceConfigElementResolved;
+ }
+
+ private List<XmlElement> getConfigNodes(XmlElement moduleElement, String moduleNamespace, String name,
+ List<XmlElement> recognisedChildren, List<XmlElement> typeAndName) {
+ List<XmlElement> foundConfigNodes = moduleElement.getChildElementsWithinNamespace(name, moduleNamespace);
+ if (foundConfigNodes.isEmpty()) {
+ logger.debug("No config nodes {}:{} found in {}", moduleNamespace, name, moduleElement);
+ logger.debug("Trying lookup of config nodes without specified namespace");
+ foundConfigNodes = moduleElement.getChildElementsWithinNamespace(name,
+ XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+ // In case module type or name element is not present in config it
+ // would be matched with config type or name
+ // We need to remove config type and name from available module
+ // config elements
+ foundConfigNodes.removeAll(typeAndName);
+ logger.debug("Found {} config nodes {} without specified namespace in {}", foundConfigNodes.size(), name,
+ moduleElement);
+ } else {
+ List<XmlElement> foundWithoutNamespaceNodes = moduleElement.getChildElementsWithinNamespace(name,
+ XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+ foundWithoutNamespaceNodes.removeAll(typeAndName);
+ Preconditions.checkState(foundWithoutNamespaceNodes.isEmpty(),
+ "Element %s present multiple times with different namespaces: %s, %s", name, foundConfigNodes,
+ foundWithoutNamespaceNodes);
+ }
+
+ recognisedChildren.addAll(foundConfigNodes);
+ return foundConfigNodes;
+ }
+
+ private static Map<String, AttributeIfc> reverseMap(Map<String, AttributeIfc> yangNameToAttr) {
+ Map<String, AttributeIfc> reversednameToAtr = Maps.newHashMap();
+
+ for (Entry<String, AttributeIfc> entry : yangNameToAttr.entrySet()) {
+ reversednameToAtr.put(entry.getValue().getUpperCaseCammelCase(), entry.getValue());
+ }
+
+ return reversednameToAtr;
+ }
+
+}
--- /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.config;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditStrategyType;
+
+import java.util.Map;
+
+/**
+ * Parsed xml element containing whole configuration for an instance of some
+ * module. Contains preferred edit strategy type.
+ */
+public class InstanceConfigElementResolved {
+
+ private final EditStrategyType editStrategy;
+ private final Map<String, AttributeConfigElement> configuration;
+
+ public InstanceConfigElementResolved(String strat, Map<String, AttributeConfigElement> configuration) {
+ EditStrategyType valueOf = checkStrategy(strat);
+ this.editStrategy = valueOf;
+ this.configuration = configuration;
+ }
+
+ EditStrategyType checkStrategy(String strat) {
+ EditStrategyType valueOf = EditStrategyType.valueOf(strat);
+ if (EditStrategyType.defaultStrategy().isEnforcing()) {
+ Preconditions
+ .checkArgument(
+ valueOf == EditStrategyType.defaultStrategy(),
+ "With "
+ + EditStrategyType.defaultStrategy()
+ + " as "
+ + EditConfigXmlParser.DEFAULT_OPERATION_KEY
+ + " operations on module elements are not permitted since the default option is restrictive");
+ }
+ return valueOf;
+ }
+
+ public InstanceConfigElementResolved(Map<String, AttributeConfigElement> configuration) {
+ editStrategy = EditStrategyType.defaultStrategy();
+ this.configuration = configuration;
+ }
+
+ public EditConfigStrategy getEditStrategy() {
+ return editStrategy.getFittingStrategy();
+ }
+
+ public Map<String, AttributeConfigElement> getConfiguration() {
+ return configuration;
+ }
+}
--- /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.config;
+
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.Collection;
+import java.util.Collections;
+
+public class ModuleConfig {
+
+ private final String moduleName;
+ private final InstanceConfig instanceConfig;
+ private final Collection<String> providedServices;
+
+ public ModuleConfig(String moduleName, InstanceConfig mbeanMapping, Collection<String> providedServices) {
+ this.moduleName = moduleName;
+ this.instanceConfig = mbeanMapping;
+ this.providedServices = providedServices;
+ }
+
+ public ModuleConfig(String key, InstanceConfig instanceConfig) {
+ this(key, instanceConfig, Collections.<String> emptyList());
+ }
+
+ public InstanceConfig getMbeanMapping() {
+ return instanceConfig;
+ }
+
+ public Collection<String> getProvidedServices() {
+ return providedServices;
+ }
+
+ public Element toXml(ObjectName instanceON, Services depTracker, Document document, String namespace) {
+ Element root = document.createElement(XmlNetconfConstants.MODULE_KEY);
+ // Xml.addNamespaceAttr(document, root, namespace);
+
+ final String prefix = getPrefix(namespace);
+ Element typeElement = XmlUtil.createPrefixedTextElement(document, XmlNetconfConstants.TYPE_KEY, prefix,
+ moduleName);
+ XmlUtil.addPrefixedNamespaceAttr(typeElement, prefix, namespace);
+ // Xml.addNamespaceAttr(document, typeElement,
+ // XMLUtil.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+ root.appendChild(typeElement);
+
+ Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY,
+ ObjectNameUtil.getInstanceName(instanceON));
+ // Xml.addNamespaceAttr(document, nameElement,
+ // XMLUtil.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+ root.appendChild(nameElement);
+
+ root = instanceConfig.toXml(instanceON, depTracker, namespace, document, root);
+
+ return root;
+ }
+
+ private String getPrefix(String namespace) {
+ // if(namespace.contains(":")==false)
+ return "prefix";
+ // return namespace.substring(namespace.lastIndexOf(':') + 1,
+ // namespace.length());
+
+ }
+
+ public ModuleElementResolved fromXml(XmlElement moduleElement, Services depTracker, String instanceName,
+ String moduleNamespace) {
+
+ InstanceConfigElementResolved ice = instanceConfig.fromXml(moduleElement, depTracker, moduleNamespace);
+ return new ModuleElementResolved(instanceName, ice);
+ }
+
+}
--- /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.config;
+
+public class ModuleElementResolved {
+
+ private final String instanceName;
+ private final InstanceConfigElementResolved instanceConfigElementResolved;
+
+ public ModuleElementResolved(String instanceName, InstanceConfigElementResolved instanceConfigElementResolved) {
+ this.instanceName = instanceName;
+ this.instanceConfigElementResolved = instanceConfigElementResolved;
+ }
+
+ public String getInstanceName() {
+ return instanceName;
+ }
+
+ public InstanceConfigElementResolved getInstanceConfigElementResolved() {
+ return instanceConfigElementResolved;
+ }
+
+}
--- /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.config;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.annotation.Nullable;
+import javax.management.ObjectName;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class Services {
+
+ private static final String PROVIDER_KEY = "provider";
+ private static final String NAME_KEY = "name";
+ public static final String TYPE_KEY = "type";
+ public static final String SERVICE_KEY = "service";
+
+ private long suffix = 1;
+
+ private final Map<ServiceInstance, String> instanceToRef = Maps.newHashMap();
+ private final Map<String/* ServiceName */, Map<String/* refName */, ServiceInstance>> serviceNameToRefNameToInstance = Maps
+ .newHashMap();
+
+ public String addServiceEntry(String serviceName, ObjectName on) {
+
+ String moduleName = on.getKeyProperty("moduleFactoryName");
+ String instanceName = on.getKeyProperty("instanceName");
+
+ return addServiceEntry(serviceName, moduleName, instanceName);
+ }
+
+ public String addServiceEntry(String serviceName, String moduleName, String instanceName) {
+ ServiceInstance serviceInstance = new ServiceInstance(moduleName, instanceName);
+ serviceInstance.setServiceName(serviceName);
+
+ String refName = instanceToRef.get(serviceInstance);
+
+ Map<String, ServiceInstance> refNameToInstance = serviceNameToRefNameToInstance.get(serviceName);
+ if (refNameToInstance == null) {
+ refNameToInstance = Maps.newHashMap();
+ serviceNameToRefNameToInstance.put(serviceName, refNameToInstance);
+ }
+
+ if (refName != null) {
+ if (serviceNameToRefNameToInstance.get(serviceName).containsKey(moduleName) == false) {
+ refNameToInstance.put(refName, serviceInstance);
+ }
+ return refName;
+ } else {
+ refName = "ref_" + instanceName;
+
+ final Set<String> refNamesAsSet = toSet(instanceToRef.values());
+ if (refNamesAsSet.contains(refName)) {
+ refName = findAvailableRefName(refName, refNamesAsSet);
+ }
+
+ instanceToRef.put(serviceInstance, refName);
+ refNameToInstance.put(refName, serviceInstance);
+
+ return refName;
+ }
+ }
+
+ private Set<String> toSet(Collection<String> values) {
+ Set<String> refNamesAsSet = Sets.newHashSet();
+
+ for (String refName : values) {
+ boolean resultAdd = refNamesAsSet.add(refName);
+ Preconditions.checkState(resultAdd,
+ "Error occurred building services element, reference name {} was present twice", refName);
+ }
+
+ return refNamesAsSet;
+ }
+
+ public ServiceInstance getByServiceAndRefName(String serviceName, String refName) {
+ Map<String, ServiceInstance> refNameToInstance = serviceNameToRefNameToInstance.get(serviceName);
+ Preconditions.checkArgument(refNameToInstance != null, "No serviceInstances mapped to " + serviceName + " , "
+ + serviceNameToRefNameToInstance.keySet());
+
+ ServiceInstance serviceInstance = refNameToInstance.get(refName);
+ Preconditions.checkArgument(serviceInstance != null, "No serviceInstance mapped to " + refName
+ + " under service name " + serviceName + " , " + refNameToInstance.keySet());
+ return serviceInstance;
+ }
+
+ // TODO hide getMappedServices, call it explicitly in toXml
+
+ public Map<String, Map<String, String>> getMappedServices() {
+ Map<String, Map<String, String>> retVal = Maps.newHashMap();
+
+ for (String serviceName : serviceNameToRefNameToInstance.keySet()) {
+
+ Map<String, String> innerRetVal = Maps.transformValues(serviceNameToRefNameToInstance.get(serviceName),
+ new Function<ServiceInstance, String>() {
+ @Nullable
+ @Override
+ public String apply(@Nullable ServiceInstance serviceInstance) {
+ return serviceInstance.toString();
+ }
+ });
+ retVal.put(serviceName, innerRetVal);
+ }
+
+ return retVal;
+ }
+
+ // TODO hide resolveServices, call it explicitly in fromXml
+
+ public static Services resolveServices(Map<String, Map<String, String>> mappedServices) {
+ Services tracker = new Services();
+
+ for (Entry<String, Map<String, String>> serviceEntry : mappedServices.entrySet()) {
+
+ String serviceName = serviceEntry.getKey();
+ for (Entry<String, String> refEntry : serviceEntry.getValue().entrySet()) {
+
+ Map<String, ServiceInstance> refNameToInstance = tracker.serviceNameToRefNameToInstance
+ .get(serviceName);
+ if (refNameToInstance == null) {
+ refNameToInstance = Maps.newHashMap();
+ tracker.serviceNameToRefNameToInstance.put(serviceName, refNameToInstance);
+ }
+
+ String refName = refEntry.getKey();
+ Preconditions.checkState(false == refNameToInstance.containsKey(refName),
+ "Duplicate reference name to service " + refName + " under service " + serviceName);
+ ServiceInstance serviceInstance = ServiceInstance.fromString(refEntry.getValue());
+ refNameToInstance.put(refName, serviceInstance);
+
+ tracker.instanceToRef.put(serviceInstance, refEntry.getKey());
+ }
+ }
+ return tracker;
+ }
+
+ public static Map<String, Map<String, String>> fromXml(XmlElement xml) {
+ Map<String, Map<String, String>> retVal = Maps.newHashMap();
+
+ List<XmlElement> services = xml.getChildElements(SERVICE_KEY);
+ xml.checkUnrecognisedElements(services);
+
+ for (XmlElement service : services) {
+
+ XmlElement typeElement = service.getOnlyChildElement(TYPE_KEY);
+ String serviceName = typeElement.getTextContent();
+
+ Map<String, String> innerMap = Maps.newHashMap();
+ retVal.put(serviceName, innerMap);
+
+ List<XmlElement> instances = service.getChildElements(XmlNetconfConstants.INSTANCE_KEY);
+ service.checkUnrecognisedElements(instances, typeElement);
+
+ for (XmlElement instance : instances) {
+ XmlElement nameElement = instance.getOnlyChildElement(NAME_KEY);
+ String refName = nameElement.getTextContent();
+
+ XmlElement providerElement = instance.getOnlyChildElement(PROVIDER_KEY);
+ String providerName = providerElement.getTextContent();
+
+ instance.checkUnrecognisedElements(nameElement, providerElement);
+
+ innerMap.put(refName, providerName);
+ }
+ }
+
+ return retVal;
+ }
+
+ private String findAvailableRefName(String refName, Set<String> refNamesAsSet) {
+ String intitialRefName = refName;
+
+ while (true) {
+ refName = intitialRefName + "_" + suffix++;
+ if (refNamesAsSet.contains(refName) == false)
+ return refName;
+ }
+ }
+
+ public Element toXml(Map<String, Map<String, String>> mappedServices, Document document) {
+ Element root = document.createElement(XmlNetconfConstants.SERVICES_KEY);
+ XmlUtil.addNamespaceAttr(root, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+
+ for (Entry<String, Map<String, String>> serviceEntry : mappedServices.entrySet()) {
+ Element serviceElement = document.createElement(SERVICE_KEY);
+ root.appendChild(serviceElement);
+
+ Element typeElement = XmlUtil.createTextElement(document, TYPE_KEY, serviceEntry.getKey());
+ serviceElement.appendChild(typeElement);
+
+ for (Entry<String, String> instanceEntry : serviceEntry.getValue().entrySet()) {
+ Element instanceElement = document.createElement(XmlNetconfConstants.INSTANCE_KEY);
+ serviceElement.appendChild(instanceElement);
+
+ Element nameElement = XmlUtil.createTextElement(document, NAME_KEY, instanceEntry.getKey());
+ instanceElement.appendChild(nameElement);
+
+ Element providerElement = XmlUtil.createTextElement(document, PROVIDER_KEY, instanceEntry.getValue());
+ instanceElement.appendChild(providerElement);
+ }
+ }
+
+ return root;
+ }
+
+ public static final class ServiceInstance {
+ public ServiceInstance(String moduleName, String instanceName) {
+ this.moduleName = moduleName;
+ this.instanceName = instanceName;
+ }
+
+ public static ServiceInstance fromString(String instanceId) {
+ instanceId = instanceId.trim();
+ Matcher matcher = p.matcher(instanceId);
+ Preconditions.checkArgument(matcher.matches(), "Unexpected format for provider, expected " + p.toString()
+ + " but was " + instanceId);
+ String factoryName = matcher.group(1);
+ String instanceName = matcher.group(2);
+ return new ServiceInstance(factoryName, instanceName);
+ }
+
+ private final String moduleName, instanceName;
+ private String serviceName;
+
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ public void setServiceName(String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ public String getModuleName() {
+ return moduleName;
+ }
+
+ public String getInstanceName() {
+ return instanceName;
+ }
+
+ private static final String blueprint = "/" + XmlNetconfConstants.CONFIG_KEY + "/"
+ + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "["
+ + XmlNetconfConstants.NAME_KEY + "='%s']/" + XmlNetconfConstants.INSTANCE_KEY + "["
+ + XmlNetconfConstants.NAME_KEY + "='%s']";
+
+ private static final String blueprintR = "/" + XmlNetconfConstants.CONFIG_KEY + "/"
+ + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\["
+ + XmlNetconfConstants.NAME_KEY + "='%s'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\["
+ + XmlNetconfConstants.NAME_KEY + "='%s'\\]";
+
+ private static final Pattern p = Pattern.compile(String.format(blueprintR, "(.+)", "(.+)"));
+
+ @Override
+ public String toString() {
+ return String.format(blueprint, moduleName, instanceName);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((instanceName == null) ? 0 : instanceName.hashCode());
+ result = prime * result + ((moduleName == null) ? 0 : moduleName.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ServiceInstance other = (ServiceInstance) obj;
+ if (instanceName == null) {
+ if (other.instanceName != null)
+ return false;
+ } else if (!instanceName.equals(other.instanceName))
+ return false;
+ if (moduleName == null) {
+ if (other.moduleName != null)
+ return false;
+ } else if (!moduleName.equals(other.moduleName))
+ return false;
+ return true;
+ }
+
+ }
+
+}
--- /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.rpc;
+
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeReadingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.ObjectXmlReader;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.ObjectResolver;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+
+import javax.management.openmbean.OpenType;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public final class InstanceRuntimeRpc {
+
+ private final Map<String, AttributeIfc> yangToAttrConfig;
+ private final Rpc rpc;
+
+ public InstanceRuntimeRpc(Rpc rpc) {
+ this.yangToAttrConfig = map(rpc.getParameters());
+ this.rpc = rpc;
+ }
+
+ private Map<String, AttributeIfc> map(List<JavaAttribute> parameters) {
+ Map<String, AttributeIfc> mapped = Maps.newHashMap();
+ for (JavaAttribute javaAttribute : parameters) {
+ mapped.put(javaAttribute.getAttributeYangName(), javaAttribute);
+ }
+ return mapped;
+ }
+
+ private void resolveConfiguration(Map<String, AttributeConfigElement> mappedConfig) {
+
+ // TODO make field, resolvingStrategies can be instantiated only once
+ Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> resolvingStrategies = new ObjectResolver(null)
+ .prepareResolving(yangToAttrConfig);
+ // TODO make constructor for object resolver without service tracker
+ for (Entry<String, AttributeConfigElement> configDefEntry : mappedConfig.entrySet()) {
+ try {
+
+ AttributeResolvingStrategy<?, ? extends OpenType<?>> attributeResolvingStrategy = resolvingStrategies
+ .get(configDefEntry.getKey());
+
+ configDefEntry.getValue().resolveValue(attributeResolvingStrategy, configDefEntry.getKey());
+ configDefEntry.getValue().setJmxName(
+ yangToAttrConfig.get(configDefEntry.getKey()).getUpperCaseCammelCase());
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to resolve value " + configDefEntry.getValue()
+ + " to attribute " + configDefEntry.getKey(), e);
+ }
+ }
+ }
+
+ public Map<String, AttributeConfigElement> fromXml(XmlElement configRootNode) {
+ Map<String, AttributeConfigElement> retVal = Maps.newHashMap();
+
+ Map<String, AttributeReadingStrategy> strats = new ObjectXmlReader().prepareReading(yangToAttrConfig);
+
+ for (Entry<String, AttributeReadingStrategy> readStratEntry : strats.entrySet()) {
+ List<XmlElement> configNodes = configRootNode.getChildElements(readStratEntry.getKey());
+ AttributeConfigElement readElement = readStratEntry.getValue().readElement(configNodes);
+ retVal.put(readStratEntry.getKey(), readElement);
+ }
+
+ resolveConfiguration(retVal);
+ return retVal;
+ }
+
+ public String getName() {
+ return rpc.getName();
+ }
+
+ public String getReturnType() {
+ return rpc.getReturnType();
+ }
+
+}
--- /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.rpc;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
+
+import java.util.Map;
+
+public final class ModuleRpcs {
+
+ Map<String, String> yangToJavaNames = Maps.newHashMap();
+ Map<String, Map<String, InstanceRuntimeRpc>> rpcMapping = Maps.newHashMap();
+
+ public void addNameMapping(RuntimeBeanEntry runtimeEntry) {
+ String yangName = runtimeEntry.getYangName();
+ Preconditions.checkState(yangToJavaNames.containsKey(yangName) == false,
+ "RuntimeBean %s found twice in same namespace", yangName);
+ yangToJavaNames.put(yangName, runtimeEntry.getJavaNamePrefix());
+ }
+
+ public void addRpc(RuntimeBeanEntry runtimeEntry, Rpc rpc) {
+ String yangName = runtimeEntry.getYangName();
+ Map<String, InstanceRuntimeRpc> map = rpcMapping.get(yangName);
+ if (map == null) {
+ map = Maps.newHashMap();
+ rpcMapping.put(yangName, map);
+ }
+
+ Preconditions.checkState(map.containsKey(rpc.getYangName()) == false, "Rpc %s for runtime bean %s added twice",
+ rpc.getYangName(), yangName);
+ map.put(rpc.getYangName(), new InstanceRuntimeRpc(rpc));
+ }
+
+ public String getRbeJavaName(String yangName) {
+ String javaName = yangToJavaNames.get(yangName);
+ Preconditions.checkState(javaName != null,
+ "No runtime bean entry found under yang name %s, available yang names %s", yangName,
+ yangToJavaNames.keySet());
+ return javaName;
+ }
+
+ public InstanceRuntimeRpc getRpc(String rbeName, String rpcName) {
+ Map<String, InstanceRuntimeRpc> rpcs = rpcMapping.get(rbeName);
+ Preconditions.checkState(rpcs != null, "No rpcs found for runtime bean %s", rbeName);
+ InstanceRuntimeRpc rpc = rpcs.get(rpcName);
+ Preconditions.checkState(rpc != null, "No rpc found for runtime bean %s with name %s", rbeName, rpcName);
+ return rpc;
+ }
+}
--- /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.rpc;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpcElementResolved;
+
+import java.util.Map;
+
+public class Rpcs {
+ private final Map<String, Map<String, ModuleRpcs>> mappedRpcs;
+
+ public Rpcs(Map<String, Map<String, ModuleRpcs>> mappedRpcs) {
+ super();
+ this.mappedRpcs = mappedRpcs;
+ }
+
+ public ModuleRpcs getRpcMapping(RuntimeRpcElementResolved id) {
+ Map<String, ModuleRpcs> modules = mappedRpcs.get(id.getNamespace());
+ Preconditions.checkState(modules != null, "No modules found for namespace %s", id.getNamespace());
+ ModuleRpcs rpcMapping = modules.get(id.getModuleName());
+ Preconditions.checkState(modules != null, "No module %s found for namespace %s", id.getModuleName(),
+ id.getNamespace());
+
+ return rpcMapping;
+ }
+}
--- /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.runtime;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class InstanceRuntime {
+
+ /**
+ *
+ */
+ private static final String KEY_ATTRIBUTE_KEY = "key";
+
+ private final InstanceConfig instanceMapping;
+ private final Map<String, InstanceRuntime> childrenMappings;
+ private final Map<String, String> jmxToYangChildRbeMapping;
+
+ public InstanceRuntime(InstanceConfig instanceMapping, Map<String, InstanceRuntime> childrenMappings,
+ Map<String, String> jmxToYangChildRbeMapping) {
+ this.instanceMapping = instanceMapping;
+ this.childrenMappings = childrenMappings;
+ this.jmxToYangChildRbeMapping = jmxToYangChildRbeMapping;
+ }
+
+ /**
+ * Finds all children runtime beans, same properties and values as current
+ * root + any number of additional properties
+ */
+ private Set<ObjectName> findChildren(ObjectName innerRootBean, Set<ObjectName> childRbeOns) {
+ final Hashtable<String, String> wantedProperties = innerRootBean.getKeyPropertyList();
+
+ return Sets.newHashSet(Collections2.filter(childRbeOns, new Predicate<ObjectName>() {
+
+ @Override
+ public boolean apply(ObjectName on) {
+ Hashtable<String, String> localProperties = on.getKeyPropertyList();
+ for (Entry<String, String> propertyEntry : wantedProperties.entrySet()) {
+ if (!localProperties.containsKey(propertyEntry.getKey()))
+ return false;
+ if (!localProperties.get(propertyEntry.getKey()).equals(propertyEntry.getValue()))
+ return false;
+ if (localProperties.size() <= wantedProperties.size())
+ return false;
+ }
+ return true;
+ }
+ }));
+ }
+
+ /**
+ * Finds next level root runtime beans, beans that have the same properties
+ * as current root + one additional
+ */
+ private Set<ObjectName> getRootBeans(Set<ObjectName> childRbeOns, final String string, final int keyListSize) {
+ return Sets.newHashSet(Collections2.filter(childRbeOns, new Predicate<ObjectName>() {
+
+ @Override
+ public boolean apply(ObjectName on) {
+ if (on.getKeyPropertyList().size() != keyListSize + 1)
+ return false;
+ if (!on.getKeyPropertyList().containsKey(string))
+ return false;
+ return true;
+ }
+ }));
+ }
+
+ public Element toXml(ObjectName rootOn, Set<ObjectName> childRbeOns, Document document) {
+ return toXml(rootOn, childRbeOns, document, null, null);
+ }
+
+ public Element toXml(ObjectName rootOn, Set<ObjectName> childRbeOns, Document document, String instanceIndex,
+ String keyName) {
+ Element xml = document.createElement(keyName == null ? XmlNetconfConstants.DATA_KEY : keyName);
+ // TODO namespace
+ xml = instanceMapping.toXml(rootOn, null, "namespace", document, xml);
+
+ if (instanceIndex != null) {
+ xml.setAttribute(KEY_ATTRIBUTE_KEY, instanceIndex);
+ }
+
+ for (Entry<String, InstanceRuntime> childMappingEntry : childrenMappings.entrySet()) {
+ Set<ObjectName> innerRootBeans = getRootBeans(childRbeOns, childMappingEntry.getKey(), rootOn
+ .getKeyPropertyList().size());
+
+ for (ObjectName objectName : innerRootBeans) {
+ Set<ObjectName> innerChildRbeOns = findChildren(objectName, childRbeOns);
+ String runtimeInstanceIndex = objectName.getKeyProperty(childMappingEntry.getKey());
+
+ String elementName = jmxToYangChildRbeMapping.get(childMappingEntry.getKey());
+ xml.appendChild(childMappingEntry.getValue().toXml(objectName, innerChildRbeOns, document,
+ runtimeInstanceIndex, elementName));
+ }
+ }
+
+ return xml;
+ }
+
+}
--- /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.runtime;
+
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+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.Set;
+
+public class ModuleRuntime {
+
+ private final String moduleName;
+ private final InstanceRuntime instanceRuntime;
+
+ public ModuleRuntime(String moduleName, InstanceRuntime instanceRuntime) {
+ this.moduleName = moduleName;
+ this.instanceRuntime = instanceRuntime;
+ }
+
+ public InstanceRuntime getMbeanMapping() {
+ return instanceRuntime;
+ }
+
+ private ObjectName findRoot(Collection<ObjectName> runtimeBeanOns) {
+ for (ObjectName objectName : runtimeBeanOns) {
+ if (objectName.getKeyPropertyList().size() == 3)
+ return objectName;
+ }
+ throw new IllegalStateException("Root runtime bean not found among " + runtimeBeanOns);
+ }
+
+ public Element toXml(String namespace, Multimap<String, ObjectName> instances, Document document) {
+ Element root = document.createElement(XmlNetconfConstants.MODULE_KEY);
+ XmlUtil.addNamespaceAttr(root, namespace);
+
+ Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, moduleName);
+ root.appendChild(nameElement);
+
+ for (String instanceName : instances.keySet()) {
+ Element instance = document.createElement(XmlNetconfConstants.INSTANCE_KEY);
+
+ Element innerNameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, instanceName);
+ instance.appendChild(innerNameElement);
+
+ Collection<ObjectName> runtimeBeanOns = instances.get(instanceName);
+ ObjectName rootName = findRoot(runtimeBeanOns);
+
+ Set<ObjectName> childrenRuntimeBeans = Sets.newHashSet(runtimeBeanOns);
+ childrenRuntimeBeans.remove(rootName);
+
+ instance.appendChild(instanceRuntime.toXml(rootName, childrenRuntimeBeans, document));
+
+ root.appendChild(instance);
+ }
+
+ return root;
+ }
+
+}
--- /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.runtime;
+
+import com.google.common.collect.HashMultimap;
+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.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.Map;
+import java.util.Set;
+
+public class Runtime {
+
+ private final Map<String, Map<String, ModuleRuntime>> moduleRuntimes;
+
+ public Runtime(Map<String, Map<String, ModuleRuntime>> moduleRuntimes) {
+ this.moduleRuntimes = moduleRuntimes;
+ }
+
+ private Map<String, Multimap<String, ObjectName>> mapInstancesToModules(Set<ObjectName> instancesToMap) {
+ Map<String, Multimap<String, ObjectName>> retVal = Maps.newHashMap();
+
+ for (ObjectName objectName : instancesToMap) {
+ String moduleName = ObjectNameUtil.getFactoryName(objectName);
+
+ Multimap<String, ObjectName> multimap = retVal.get(moduleName);
+ if (multimap == null) {
+ multimap = HashMultimap.create();
+ retVal.put(moduleName, multimap);
+ }
+
+ String instanceName = ObjectNameUtil.getInstanceName(objectName);
+
+ multimap.put(instanceName, objectName);
+ }
+
+ return retVal;
+ }
+
+ public Element toXml(Set<ObjectName> instancesToMap, Document document) {
+ Element root = document.createElement(XmlNetconfConstants.DATA_KEY);
+
+ Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY);
+ XmlUtil.addNamespaceAttr(modulesElement,
+ XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+ root.appendChild(modulesElement);
+
+ Map<String, Multimap<String, ObjectName>> moduleToInstances = mapInstancesToModules(instancesToMap);
+
+ for (String localNamespace : moduleRuntimes.keySet()) {
+ for (String moduleName : moduleRuntimes.get(localNamespace).keySet()) {
+ Multimap<String, ObjectName> instanceToRbe = moduleToInstances.get(moduleName);
+
+ if (instanceToRbe == null)
+ continue;
+
+ ModuleRuntime moduleRuntime = moduleRuntimes.get(localNamespace).get(moduleName);
+ Element innerXml = moduleRuntime.toXml(localNamespace, instanceToRbe, document);
+ modulesElement.appendChild(innerXml);
+ }
+ }
+
+ return root;
+ }
+
+}
--- /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.operations;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public abstract class AbstractConfigNetconfOperation extends AbstractNetconfOperation {
+
+ protected final ConfigRegistryClient configRegistryClient;
+
+ protected AbstractConfigNetconfOperation(ConfigRegistryClient configRegistryClient,
+ String netconfSessionIdForReporting) {
+ super(netconfSessionIdForReporting);
+ this.configRegistryClient = configRegistryClient;
+ }
+
+ @Override
+ protected HandlingPriority canHandle(String operationName, String operationNamespace) {
+ // TODO check namespace
+ return operationName.equals(getOperationName()) ? HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY
+ : HandlingPriority.CANNOT_HANDLE;
+ }
+
+ protected abstract String getOperationName();
+
+ @Override
+ protected Element handle(Document document, XmlElement operationElement, NetconfOperationRouter opRouter)
+ throws NetconfDocumentedException {
+ return handle(document, operationElement);
+ }
+
+ protected abstract Element handle(Document document, XmlElement operationElement) throws NetconfDocumentedException;
+}
--- /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.operations;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class Commit extends AbstractConfigNetconfOperation {
+
+ private static final Logger logger = LoggerFactory.getLogger(Commit.class);
+
+ private final TransactionProvider transactionProvider;
+
+ public Commit(TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient,
+ String netconfSessionIdForReporting) {
+ super(configRegistryClient, netconfSessionIdForReporting);
+ this.transactionProvider = transactionProvider;
+ }
+
+ private static void checkXml(XmlElement xml) {
+ xml.checkName(XmlNetconfConstants.COMMIT);
+ xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ }
+
+ @Override
+ protected String getOperationName() {
+ return XmlNetconfConstants.COMMIT;
+ }
+
+ @Override
+ protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+ checkXml(xml);
+
+ CommitStatus status;
+ try {
+ status = this.transactionProvider.commitTransaction();
+ } catch (final IllegalStateException e) {
+ logger.warn("Commit failed: ", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.operation_failed.name(),
+ "Operation failed. Use 'get-config' or 'edit-config' before triggering 'commit' operation");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+ ErrorSeverity.error, errorInfo);
+ } catch (final NetconfDocumentedException e) {
+ throw new NetconfDocumentedException(
+ "Unable to retrieve config snapshot after commit for persister, details: " + e.getMessage(),
+ ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error, e.getErrorInfo());
+ }
+ logger.info("Datastore {} committed successfully: {}", Datastore.candidate, status);
+
+ return document.createElement(XmlNetconfConstants.OK);
+ }
+
+}
--- /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.operations;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.CandidateDatastoreQueryStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.DatastoreQueryStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.RunningDatastoreQueryStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+
+public enum Datastore {
+
+ running, candidate;
+
+ /**
+ * @param source
+ * @param transactionProvider
+ * @return
+ */
+ public static DatastoreQueryStrategy getInstanceQueryStrategy(Datastore source,
+ TransactionProvider transactionProvider) {
+ switch (source) {
+ case running:
+ return new RunningDatastoreQueryStrategy();
+ case candidate:
+ return new CandidateDatastoreQueryStrategy(transactionProvider);
+ default:
+ throw new UnsupportedOperationException("Unimplemented datastore query strategy for " + source);
+ }
+ }
+}
--- /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.operations;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class DiscardChanges extends AbstractConfigNetconfOperation {
+
+ public static final String DISCARD = "discard-changes";
+
+ private static final Logger logger = LoggerFactory.getLogger(DiscardChanges.class);
+
+ private final TransactionProvider transactionProvider;
+
+ public DiscardChanges(final TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient,
+ String netconfSessionIdForReporting) {
+ super(configRegistryClient, netconfSessionIdForReporting);
+ this.transactionProvider = transactionProvider;
+ }
+
+ private static void fromXml(XmlElement xml) {
+ xml.checkName(DISCARD);
+ xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ }
+
+ @Override
+ protected String getOperationName() {
+ return DISCARD;
+ }
+
+ @Override
+ protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+ try {
+ fromXml(xml);
+ } catch (final IllegalArgumentException e) {
+ logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute,
+ ErrorSeverity.error, errorInfo);
+ }
+
+ try {
+ this.transactionProvider.abortTransaction();
+ } catch (final IllegalStateException e) {
+ logger.warn("Abort failed: ", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo
+ .put(ErrorTag.operation_failed.name(),
+ "Operation failed. Use 'get-config' or 'edit-config' before triggering 'discard-changes' operation");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+ ErrorSeverity.error, errorInfo);
+ }
+ logger.info("Changes discarded successfully from datastore {}", Datastore.candidate);
+
+ return document.createElement(XmlNetconfConstants.OK);
+ }
+}
--- /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.operations;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Validate extends AbstractConfigNetconfOperation {
+
+ public static final String VALIDATE = "validate";
+
+ private static final Logger logger = LoggerFactory.getLogger(Validate.class);
+
+ private final TransactionProvider transactionProvider;
+
+ public Validate(final TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient,
+ String netconfSessionIdForReporting) {
+ super(configRegistryClient, netconfSessionIdForReporting);
+ this.transactionProvider = transactionProvider;
+ }
+
+ private void checkXml(XmlElement xml) {
+ xml.checkName(VALIDATE);
+ xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+ XmlElement sourceElement = xml.getOnlyChildElement(XmlNetconfConstants.SOURCE_KEY,
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ XmlElement sourceChildNode = sourceElement.getOnlyChildElement();
+
+ sourceChildNode.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ String datastoreValue = sourceChildNode.getName();
+ Datastore sourceDatastore = Datastore.valueOf(datastoreValue);
+
+ Preconditions.checkState(sourceDatastore == Datastore.candidate, "Only " + Datastore.candidate
+ + " is supported as source for " + VALIDATE + " but was " + datastoreValue);
+ }
+
+ @Override
+ protected String getOperationName() {
+ return VALIDATE;
+ }
+
+ @Override
+ protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+ try {
+ checkXml(xml);
+ } catch (IllegalStateException e) {
+ logger.warn("Rpc error: {}", ErrorTag.missing_attribute, e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.missing_attribute.name(), "Missing value of datastore attribute");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.missing_attribute,
+ ErrorSeverity.error, errorInfo);
+ } catch (final IllegalArgumentException e) {
+ logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute,
+ ErrorSeverity.error, errorInfo);
+ }
+
+ try {
+ transactionProvider.validateTransaction();
+ } catch (ValidationException e) {
+ logger.warn("Validation failed", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.operation_failed.name(), "Validation failed");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+ ErrorSeverity.error, errorInfo);
+ } catch (IllegalStateException e) {
+ logger.warn("Validation failed", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo
+ .put(ErrorTag.operation_failed.name(),
+ "Datastore is not present. Use 'get-config' or 'edit-config' before triggering 'operations' operation");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+ ErrorSeverity.error, errorInfo);
+
+ }
+
+ logger.info("Datastore {} validated successfully", Datastore.candidate);
+
+ return document.createElement(XmlNetconfConstants.OK);
+ }
+}
--- /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.operations.editconfig;
+
+import java.util.Map;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractEditConfigStrategy implements EditConfigStrategy {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractEditConfigStrategy.class);
+
+ @Override
+ public void executeConfiguration(String module, String instance, Map<String, AttributeConfigElement> configuration,
+ ConfigTransactionClient ta) {
+
+ try {
+ ObjectName on = ta.lookupConfigBean(module, instance);
+ logger.debug("ServiceInstance for {} {} located successfully under {}", module, instance, on);
+ executeStrategy(configuration, ta, on);
+ } catch (InstanceNotFoundException e) {
+ handleMissingInstance(configuration, ta, module, instance);
+ }
+
+ }
+
+ abstract void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+ String module, String instance);
+
+ abstract void executeStrategy(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+ ObjectName objectName);
+
+}
--- /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.operations.editconfig;
+
+import java.util.Map;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DeleteEditConfigStrategy extends AbstractEditConfigStrategy {
+
+ private static final Logger logger = LoggerFactory.getLogger(DeleteEditConfigStrategy.class);
+
+ @Override
+ void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+ String module, String instance) {
+ throw new IllegalStateException("Unable to delete " + module + ":" + instance + " , ServiceInstance not found");
+ }
+
+ @Override
+ void executeStrategy(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta, ObjectName on) {
+ try {
+ ta.destroyModule(on);
+ logger.debug("ServiceInstance {} deleted successfully", on);
+ } catch (InstanceNotFoundException e) {
+ throw new IllegalStateException("Unable to delete " + on, e);
+ }
+ }
+}
--- /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.operations.editconfig;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class EditConfig extends AbstractConfigNetconfOperation {
+
+ private static final Logger logger = LoggerFactory.getLogger(EditConfig.class);
+
+ private final YangStoreSnapshot yangStoreSnapshot;
+
+ private final TransactionProvider transactionProvider;
+ private EditConfigXmlParser editConfigXmlParser;
+
+ public EditConfig(YangStoreSnapshot yangStoreSnapshot, TransactionProvider transactionProvider,
+ ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) {
+ super(configRegistryClient, netconfSessionIdForReporting);
+ this.yangStoreSnapshot = yangStoreSnapshot;
+ this.transactionProvider = transactionProvider;
+ this.editConfigXmlParser = new EditConfigXmlParser();
+ }
+
+ @VisibleForTesting
+ Element getResponseInternal(final Document document,
+ final EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
+ if (editConfigExecution.shouldTest()) {
+ executeTests(configRegistryClient, editConfigExecution);
+ }
+
+ if (editConfigExecution.shouldSet()) {
+ executeSet(configRegistryClient, editConfigExecution);
+ }
+
+ logger.info("Operation {} successful", EditConfigXmlParser.EDIT_CONFIG);
+
+ return document.createElement(XmlNetconfConstants.OK);
+ }
+
+ private void executeSet(ConfigRegistryClient configRegistryClient,
+ EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
+ try {
+ set(configRegistryClient, editConfigExecution);
+ } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) {
+ logger.warn("Set phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage());
+ throw new NetconfDocumentedException("Test phase: " + e.getMessage(), e, ErrorType.application,
+ ErrorTag.operation_failed, ErrorSeverity.error, errorInfo);
+ }
+ logger.debug("Set phase for {} operation successful", EditConfigXmlParser.EDIT_CONFIG);
+ }
+
+ private void executeTests(ConfigRegistryClient configRegistryClient,
+ EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
+ try {
+ test(configRegistryClient, editConfigExecution.resolvedXmlElements);
+ } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) {
+ logger.warn("Test phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage());
+ throw new NetconfDocumentedException("Test phase: " + e.getMessage(), e, ErrorType.application,
+ ErrorTag.operation_failed, ErrorSeverity.error, errorInfo);
+ }
+ logger.debug("Test phase for {} operation successful", EditConfigXmlParser.EDIT_CONFIG);
+ }
+
+ private void test(ConfigRegistryClient configRegistryClient,
+ Map<String, Multimap<String, ModuleElementResolved>> resolvedModules) {
+ ObjectName taON = transactionProvider.getTestTransaction();
+ try {
+
+ // default strategy = replace wipes config
+ if (EditStrategyType.defaultStrategy() == EditStrategyType.replace) {
+ transactionProvider.wipeTestTransaction(taON);
+ }
+ setOnTransaction(configRegistryClient, resolvedModules, taON);
+ transactionProvider.validateTestTransaction(taON);
+ } finally {
+ transactionProvider.abortTestTransaction(taON);
+ }
+ }
+
+ private void set(ConfigRegistryClient configRegistryClient,
+ EditConfigXmlParser.EditConfigExecution editConfigExecution) {
+ ObjectName taON = transactionProvider.getOrCreateTransaction();
+
+ // default strategy = replace wipes config
+ if (EditStrategyType.defaultStrategy() == EditStrategyType.replace) {
+ transactionProvider.wipeTransaction();
+ }
+ setOnTransaction(configRegistryClient, editConfigExecution.resolvedXmlElements, taON);
+ }
+
+ private void setOnTransaction(ConfigRegistryClient configRegistryClient,
+ Map<String, Multimap<String, ModuleElementResolved>> resolvedXmlElements, ObjectName taON) {
+ ConfigTransactionClient ta = configRegistryClient.getConfigTransactionClient(taON);
+
+ for (Multimap<String, ModuleElementResolved> modulesToResolved : resolvedXmlElements.values()) {
+ for (Entry<String, ModuleElementResolved> moduleToResolved : modulesToResolved.entries()) {
+ String moduleName = moduleToResolved.getKey();
+
+ ModuleElementResolved moduleElementResolved = moduleToResolved.getValue();
+ String instanceName = moduleElementResolved.getInstanceName();
+
+ InstanceConfigElementResolved ice = moduleElementResolved.getInstanceConfigElementResolved();
+ EditConfigStrategy strategy = ice.getEditStrategy();
+ strategy.executeConfiguration(moduleName, instanceName, ice.getConfiguration(), ta);
+ }
+ }
+ }
+
+ public static Config getConfigMapping(ConfigRegistryClient configRegistryClient,
+ Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+ Map<String, Map<String, ModuleConfig>> factories = transform(configRegistryClient, mBeanEntries);
+ return new Config(factories);
+ }
+
+ // TODO refactor
+ private 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>>() {
+
+ @Override
+ public Map<String, ModuleConfig> transformEntry(String arg0, Map<String, ModuleMXBeanEntry> arg1) {
+ return Maps.transformEntries(arg1,
+ new Maps.EntryTransformer<String, ModuleMXBeanEntry, ModuleConfig>() {
+
+ @Override
+ public ModuleConfig transformEntry(String key, ModuleMXBeanEntry value) {
+ return new ModuleConfig(key, new InstanceConfig(configRegistryClient, value
+ .getAttributes()));
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ protected String getOperationName() {
+ return EditConfigXmlParser.EDIT_CONFIG;
+ }
+
+ @Override
+ protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+
+ EditConfigXmlParser.EditConfigExecution editConfigExecution;
+ Config cfg = getConfigMapping(configRegistryClient, yangStoreSnapshot.getModuleMXBeanEntryMap());
+ try {
+ editConfigExecution = editConfigXmlParser.fromXml(xml, cfg);
+ } 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");
+ throw new NetconfDocumentedException(e.getMessage(), ErrorType.rpc, ErrorTag.missing_attribute,
+ ErrorSeverity.error, errorInfo);
+ } catch (final IllegalArgumentException e) {
+ logger.warn("Error parsing xml", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+ throw new NetconfDocumentedException(e.getMessage(), ErrorType.rpc, ErrorTag.bad_attribute,
+ ErrorSeverity.error, errorInfo);
+ } catch (final UnsupportedOperationException e) {
+ logger.warn("Unsupported", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for 'edit-config'");
+ throw new NetconfDocumentedException(e.getMessage(), ErrorType.application,
+ ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo);
+ }
+
+ return getResponseInternal(document, editConfigExecution);
+ }
+}
--- /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.operations.editconfig;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+
+import java.util.Map;
+
+public interface EditConfigStrategy {
+
+ void executeConfiguration(String module, String instance, Map<String, AttributeConfigElement> configuration,
+ ConfigTransactionClient ta);
+
+}
--- /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.operations.editconfig;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Multimap;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementResolved;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public class EditConfigXmlParser {
+
+ private static final Logger logger = LoggerFactory.getLogger(EditConfigXmlParser.class);
+
+ public static final String EDIT_CONFIG = "edit-config";
+ public static final String DEFAULT_OPERATION_KEY = "default-operation";
+ static final String ERROR_OPTION_KEY = "error-option";
+ static final String DEFAULT_ERROR_OPTION = "stop-on-error";
+ static final String TARGET_KEY = "target";
+ static final String TEST_OPTION_KEY = "test-option";
+
+ public EditConfigXmlParser() {
+ }
+
+ EditConfigXmlParser.EditConfigExecution fromXml(final XmlElement xml, final Config cfgMapping)
+ throws NetconfDocumentedException {
+
+ EditStrategyType.resetDefaultStrategy();
+
+ xml.checkName(EditConfigXmlParser.EDIT_CONFIG);
+ xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+ XmlElement targetElement = xml.getOnlyChildElementWithSameNamespace(EditConfigXmlParser.TARGET_KEY);
+ XmlElement targetChildNode = targetElement.getOnlyChildElementWithSameNamespace();
+ String datastoreValue = targetChildNode.getName();
+ Datastore targetDatastore = Datastore.valueOf(datastoreValue);
+ logger.debug("Setting {} to '{}'", EditConfigXmlParser.TARGET_KEY, targetDatastore);
+
+ // check target
+ Preconditions.checkArgument(targetDatastore == Datastore.candidate,
+ "Only %s datastore supported for edit config but was: %s", Datastore.candidate, targetDatastore);
+
+ // Test option
+ TestOption testOption;
+ Optional<XmlElement> testOptionElementOpt = xml
+ .getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.TEST_OPTION_KEY);
+ if (testOptionElementOpt.isPresent()) {
+ String testOptionValue = testOptionElementOpt.get().getTextContent();
+ testOption = EditConfigXmlParser.TestOption.getFromXmlName(testOptionValue);
+ } else {
+ testOption = EditConfigXmlParser.TestOption.getDefault();
+ }
+ logger.debug("Setting {} to '{}'", EditConfigXmlParser.TEST_OPTION_KEY, testOption);
+
+ // Error option
+ Optional<XmlElement> errorOptionElement = xml
+ .getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.ERROR_OPTION_KEY);
+ if (errorOptionElement.isPresent()) {
+ String errorOptionParsed = errorOptionElement.get().getTextContent();
+ if (false == errorOptionParsed.equals(EditConfigXmlParser.DEFAULT_ERROR_OPTION))
+ throw new UnsupportedOperationException("Only " + EditConfigXmlParser.DEFAULT_ERROR_OPTION
+ + " supported for " + EditConfigXmlParser.ERROR_OPTION_KEY + ", was " + errorOptionParsed);
+ }
+
+ // Default op
+ Optional<XmlElement> defaultContent = xml
+ .getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.DEFAULT_OPERATION_KEY);
+ if (defaultContent.isPresent())
+ EditStrategyType.setDefaultStrategy(EditStrategyType.valueOf(defaultContent.get().getTextContent()));
+
+ XmlElement configElement = xml.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.CONFIG_KEY);
+
+ return new EditConfigXmlParser.EditConfigExecution(xml, cfgMapping, configElement, testOption);
+ }
+
+ private void removeMountpointsFromConfig(XmlElement configElement, XmlElement mountpointsElement) {
+ configElement.getDomElement().removeChild(mountpointsElement.getDomElement());
+ }
+
+ @VisibleForTesting
+ static enum TestOption {
+ testOnly, set, testThenSet;
+
+ static TestOption getFromXmlName(String testOptionXmlName) {
+ switch (testOptionXmlName) {
+ case "test-only":
+ return testOnly;
+ case "test-then-set":
+ return testThenSet;
+ case "set":
+ return set;
+ default:
+ throw new IllegalArgumentException("Unsupported test option " + testOptionXmlName + " supported: "
+ + Arrays.toString(TestOption.values()));
+ }
+ }
+
+ public static TestOption getDefault() {
+ return testThenSet;
+ }
+
+ }
+
+ @VisibleForTesting
+ static class EditConfigExecution {
+ XmlElement editConfigXml;
+ Map<String, Multimap<String, ModuleElementResolved>> resolvedXmlElements;
+ TestOption testOption;
+
+ EditConfigExecution(XmlElement xml, Config configResolver, XmlElement configElement, TestOption testOption) {
+ this.editConfigXml = xml;
+ this.resolvedXmlElements = configResolver.fromXml(configElement);
+ this.testOption = testOption;
+ }
+
+ boolean shouldTest() {
+ return testOption == TestOption.testOnly || testOption == TestOption.testThenSet;
+ }
+
+ boolean shouldSet() {
+ return testOption == TestOption.set || testOption == TestOption.testThenSet;
+ }
+ }
+}
--- /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.operations.editconfig;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+
+public enum EditStrategyType {
+ // can be default
+ merge, replace, none,
+ // additional per element
+ delete, remove;
+
+ private static final Set<EditStrategyType> defaultStrats = EnumSet.of(merge, replace, none);
+
+ private static EditStrategyType defaultStrat = merge;
+
+ public static EditStrategyType defaultStrategy() {
+ return defaultStrat;
+ }
+
+ public static void setDefaultStrategy(EditStrategyType strat) {
+ Preconditions.checkArgument(strat.canBeDefault(), "Default edit strategy can be only of value " + defaultStrats
+ + ", but was " + strat);
+ defaultStrat = strat;
+ }
+
+ public static void resetDefaultStrategy() {
+ setDefaultStrategy(EditStrategyType.merge);
+ }
+
+ public boolean isEnforcing() {
+ switch (this) {
+ case merge:
+ case none:
+ case remove:
+ case delete:
+ return false;
+ case replace:
+ return true;
+
+ default:
+ throw new IllegalStateException("Default edit strategy can be only of value " + defaultStrats + " but was "
+ + this);
+ }
+ }
+
+ private static final EnumSet<EditStrategyType> defaults;
+
+ static {
+ defaults = EnumSet.of(merge, replace, none);
+ }
+
+ private boolean canBeDefault() {
+ return defaults.contains(this);
+ }
+
+ public EditConfigStrategy getFittingStrategy() {
+ switch (this) {
+ case merge:
+ return new MergeEditConfigStrategy();
+ case replace:
+ return new ReplaceEditConfigStrategy();
+ case delete:
+ return new DeleteEditConfigStrategy();
+ case remove:
+ return new RemoveEditConfigStrategy();
+ case none:
+ return new NoneEditConfigStrategy();
+ default:
+ throw new UnsupportedOperationException("Unimplemented edit config strategy" + this);
+ }
+ }
+}
--- /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.operations.editconfig;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.management.Attribute;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MergeEditConfigStrategy extends AbstractEditConfigStrategy {
+
+ private static final Logger logger = LoggerFactory.getLogger(MergeEditConfigStrategy.class);
+
+ @Override
+ void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+ String module, String instance) {
+ ObjectName on;
+ try {
+ on = ta.createModule(module, instance);
+ logger.info("New instance for {} {} created under name {}", module, instance, on);
+ executeStrategy(configuration, ta, on);
+ } catch (InstanceAlreadyExistsException e1) {
+ throw new IllegalStateException("Unable to create instance for " + module + " : " + instance);
+ }
+ }
+
+ @Override
+ void executeStrategy(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta, ObjectName on) {
+ for (Entry<String, AttributeConfigElement> configAttributeEntry : configuration.entrySet()) {
+ try {
+ AttributeConfigElement ace = configAttributeEntry.getValue();
+
+ if (!ace.getResolvedValue().isPresent()) {
+ logger.debug("Skipping attribute {} for {}", configAttributeEntry.getKey(), on);
+ continue;
+ }
+
+ Object value = ace.getResolvedValue().get();
+ ta.setAttribute(on, ace.getJmxName(), new Attribute(ace.getJmxName(), value));
+ logger.debug("Attribute {} set to {} for {}", configAttributeEntry.getKey(), value, on);
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to set attributes for " + on + ", Error with attribute "
+ + configAttributeEntry.getKey() + ":" + configAttributeEntry.getValue(), e);
+ }
+ }
+ }
+}
--- /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.operations.editconfig;
+
+import java.util.Map;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NoneEditConfigStrategy implements EditConfigStrategy {
+
+ private static final Logger logger = LoggerFactory.getLogger(NoneEditConfigStrategy.class);
+
+ @Override
+ public void executeConfiguration(String module, String instance, Map<String, AttributeConfigElement> configuration,
+ ConfigTransactionClient ta) {
+ logger.debug("Skipping configuration element for {}:{}", module, instance);
+ }
+
+}
--- /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.operations.editconfig;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+public class RemoveEditConfigStrategy extends DeleteEditConfigStrategy {
+
+ private static final Logger logger = LoggerFactory.getLogger(RemoveEditConfigStrategy.class);
+
+ @Override
+ void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+ String module, String instance) {
+ logger.warn("Unable to delete {}:{}, ServiceInstance not found", module, instance);
+ }
+}
--- /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.operations.editconfig;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.management.Attribute;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ReplaceEditConfigStrategy extends AbstractEditConfigStrategy {
+
+ private static final Logger logger = LoggerFactory.getLogger(ReplaceEditConfigStrategy.class);
+
+ @Override
+ void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+ String module, String instance) {
+ try {
+ ObjectName on = ta.createModule(module, instance);
+ logger.debug("New instance for {} {} created under name {}", module, instance, on);
+ executeStrategy(configuration, ta, on);
+ } catch (InstanceAlreadyExistsException e) {
+ logger.warn("Error creating instance {}:{}, replace failed", module, instance, e);
+ throw new IllegalStateException("Unable to create new instance for " + module + " : " + instance, e);
+ }
+ }
+
+ @Override
+ void executeStrategy(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta, ObjectName on) {
+ for (Entry<String, AttributeConfigElement> configAttributeEntry : configuration.entrySet()) {
+ try {
+ AttributeConfigElement ace = configAttributeEntry.getValue();
+
+ if (!ace.getResolvedValue().isPresent()) {
+ Object value = ace.getResolvedDefaultValue();
+ ta.setAttribute(on, ace.getJmxName(), new Attribute(ace.getJmxName(), value));
+ logger.debug("Attribute {} set to default value {} for {}", configAttributeEntry.getKey(), value,
+ on);
+ } else {
+ Object value = ace.getResolvedValue().get();
+ ta.setAttribute(on, ace.getJmxName(), new Attribute(ace.getJmxName(), value));
+ logger.debug("Attribute {} set to value {} for {}", configAttributeEntry.getKey(), value, on);
+ }
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to set attributes for " + on + ", Error with attribute "
+ + configAttributeEntry.getKey() + ":" + configAttributeEntry.getValue(), e);
+ }
+ }
+ }
+}
--- /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.operations.get;
+
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+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.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.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class Get extends AbstractConfigNetconfOperation {
+
+ public static final String GET = "get";
+
+ private final YangStoreSnapshot yangStoreSnapshot;
+ private static final Logger logger = LoggerFactory.getLogger(Get.class);
+
+ public Get(YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+ String netconfSessionIdForReporting) {
+ super(configRegistryClient, netconfSessionIdForReporting);
+ this.yangStoreSnapshot = yangStoreSnapshot;
+ }
+
+ private Map<String, Map<String, ModuleRuntime>> createModuleRuntimes(ConfigRegistryClient configRegistryClient,
+ Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+ Map<String, Map<String, ModuleRuntime>> retVal = Maps.newHashMap();
+
+ for (String namespace : mBeanEntries.keySet()) {
+
+ Map<String, ModuleRuntime> innerMap = Maps.newHashMap();
+ Map<String, ModuleMXBeanEntry> entriesFromNamespace = mBeanEntries.get(namespace);
+ for (String module : entriesFromNamespace.keySet()) {
+
+ ModuleMXBeanEntry mbe = entriesFromNamespace.get(module);
+
+ Map<RuntimeBeanEntry, InstanceConfig> cache = Maps.newHashMap();
+ RuntimeBeanEntry root = null;
+ for (RuntimeBeanEntry rbe : mbe.getRuntimeBeans()) {
+ cache.put(rbe, new InstanceConfig(configRegistryClient, rbe.getYangPropertiesToTypesMap()));
+ if (rbe.isRoot())
+ root = rbe;
+ }
+
+ if (root == null)
+ continue;
+
+ InstanceRuntime rootInstanceRuntime = createInstanceRuntime(root, cache);
+ ModuleRuntime moduleRuntime = new ModuleRuntime(module, rootInstanceRuntime);
+ innerMap.put(module, moduleRuntime);
+ }
+
+ retVal.put(namespace, innerMap);
+ }
+ return retVal;
+ }
+
+ private InstanceRuntime createInstanceRuntime(RuntimeBeanEntry root, Map<RuntimeBeanEntry, InstanceConfig> cache) {
+ Map<String, InstanceRuntime> children = Maps.newHashMap();
+ for (RuntimeBeanEntry child : root.getChildren()) {
+ children.put(child.getJavaNamePrefix(), createInstanceRuntime(child, cache));
+ }
+
+ return new InstanceRuntime(cache.get(root), children, createJmxToYangMap(root.getChildren()));
+ }
+
+ private Map<String, String> createJmxToYangMap(List<RuntimeBeanEntry> children) {
+ Map<String, String> jmxToYangNamesForChildRbe = Maps.newHashMap();
+ for (RuntimeBeanEntry rbe : children) {
+ jmxToYangNamesForChildRbe.put(rbe.getJavaNamePrefix(), rbe.getYangName());
+ }
+ return jmxToYangNamesForChildRbe;
+ }
+
+ private static void checkXml(XmlElement xml) {
+ xml.checkName(GET);
+ xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+ // Filter option - unsupported
+ if (xml.getChildElements(XmlNetconfConstants.FILTER).size() != 0)
+ throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for " + GET);
+ }
+
+ @Override
+ protected String getOperationName() {
+ return GET;
+ }
+
+ @Override
+ protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+ try {
+ checkXml(xml);
+ } catch (final IllegalArgumentException e) {
+ logger.warn("Error parsing xml", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute,
+ ErrorSeverity.error, errorInfo);
+ } catch (final UnsupportedOperationException e) {
+ logger.warn("Unsupported", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for 'get'");
+ 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,
+ yangStoreSnapshot.getModuleMXBeanEntryMap());
+ final Runtime runtime = new Runtime(moduleMappings);
+
+ final Element element = runtime.toXml(runtimeBeans, document);
+
+ logger.info("{} operation successful", GET);
+
+ return element;
+ }
+}
--- /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.operations.getconfig;
+
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+
+public class CandidateDatastoreQueryStrategy implements DatastoreQueryStrategy {
+
+ private final TransactionProvider transactionProvider;
+
+ public CandidateDatastoreQueryStrategy(TransactionProvider transactionProvider) {
+ this.transactionProvider = transactionProvider;
+ }
+
+ @Override
+ public Set<ObjectName> queryInstances(ConfigRegistryClient configRegistryClient) {
+ ObjectName on = transactionProvider.getOrCreateTransaction();
+ ConfigTransactionClient proxy = configRegistryClient.getConfigTransactionClient(on);
+ return proxy.lookupConfigBeans();
+ }
+
+}
--- /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.operations.getconfig;
+
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+
+public interface DatastoreQueryStrategy {
+
+ /**
+ * @param configRegistryClient
+ * @return
+ */
+ Set<ObjectName> queryInstances(ConfigRegistryClient configRegistryClient);
+
+}
--- /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.operations.getconfig;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class GetConfig extends AbstractConfigNetconfOperation {
+
+ public static final String GET_CONFIG = "get-config";
+
+ private final YangStoreSnapshot yangStoreSnapshot;
+ private final Optional<String> maybeNamespace;
+
+ private final TransactionProvider transactionProvider;
+
+ private static final Logger logger = LoggerFactory.getLogger(GetConfig.class);
+
+ public GetConfig(YangStoreSnapshot yangStoreSnapshot, Optional<String> maybeNamespace,
+ TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient,
+ String netconfSessionIdForReporting) {
+ super(configRegistryClient, netconfSessionIdForReporting);
+ this.yangStoreSnapshot = yangStoreSnapshot;
+ this.maybeNamespace = maybeNamespace;
+ this.transactionProvider = transactionProvider;
+ }
+
+ public static Datastore fromXml(XmlElement xml) {
+ xml.checkName(GET_CONFIG);
+ xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+ XmlElement sourceElement = xml.getOnlyChildElement(XmlNetconfConstants.SOURCE_KEY,
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ XmlElement sourceNode = sourceElement.getOnlyChildElement();
+ String sourceParsed = sourceNode.getName();
+ logger.debug("Setting source datastore to '{}'", sourceParsed);
+ Datastore sourceDatastore = Datastore.valueOf(sourceParsed);
+
+ // Filter option - unsupported
+ if (xml.getChildElements(XmlNetconfConstants.FILTER).size() != 0)
+ throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for "
+ + GET_CONFIG);
+
+ return sourceDatastore;
+
+ }
+
+ private Element getResponseInternal(final Document document, final ConfigRegistryClient configRegistryClient,
+ final Datastore source) throws NetconfDocumentedException {
+ Element dataElement = document.createElement(XmlNetconfConstants.DATA_KEY);
+ final Set<ObjectName> instances = Datastore.getInstanceQueryStrategy(source, this.transactionProvider)
+ .queryInstances(configRegistryClient);
+ final Config configMapping = new Config(transform(configRegistryClient,
+ yangStoreSnapshot.getModuleMXBeanEntryMap()));
+ dataElement = configMapping.toXml(instances, this.maybeNamespace, document, dataElement);
+
+ logger.info("{} operation successful", GET_CONFIG);
+
+ return dataElement;
+ }
+
+ // TODO refactor ... duplicate code
+ private 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>>() {
+
+ @Override
+ public Map<String, ModuleConfig> transformEntry(String arg0, Map<String, ModuleMXBeanEntry> arg1) {
+ return Maps.transformEntries(arg1,
+ new Maps.EntryTransformer<String, ModuleMXBeanEntry, ModuleConfig>() {
+
+ @Override
+ public ModuleConfig transformEntry(String key, ModuleMXBeanEntry value) {
+ return new ModuleConfig(key, new InstanceConfig(configRegistryClient, value
+ .getAttributes()), value.getProvidedServices().values());
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ protected String getOperationName() {
+ return GET_CONFIG;
+ }
+
+ @Override
+ public Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+ Datastore source;
+ try {
+ source = fromXml(xml);
+ } catch (final IllegalArgumentException e) {
+ logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute,
+ ErrorSeverity.error, errorInfo);
+ } catch (final IllegalStateException e) {
+ logger.warn("Rpc error: {}", ErrorTag.missing_attribute, e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.missing_attribute.name(), "Missing datasource attribute value");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.missing_attribute,
+ ErrorSeverity.error, errorInfo);
+ } catch (final UnsupportedOperationException e) {
+ logger.warn("Unsupported", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for get");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application,
+ ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo);
+ }
+ return getResponseInternal(document, configRegistryClient, source);
+ }
+}
--- /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.operations.getconfig;
+
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+
+public class RunningDatastoreQueryStrategy implements DatastoreQueryStrategy {
+
+ @Override
+ public Set<ObjectName> queryInstances(ConfigRegistryClient configRegistryClient) {
+ return configRegistryClient.lookupConfigBeans();
+ }
+
+}
--- /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.operations.runtimerpc;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.SimpleTypeResolver;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.SimpleAttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.InstanceRuntimeRpc;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.Rpcs;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+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 javax.management.ObjectName;
+import javax.management.openmbean.SimpleType;
+import java.util.Map;
+
+public class RuntimeRpc extends AbstractConfigNetconfOperation {
+
+ private static final Logger logger = LoggerFactory.getLogger(Commit.class);
+ public static final String CONTEXT_INSTANCE = "context-instance";
+
+ private final YangStoreSnapshot yangStoreSnapshot;
+
+ public RuntimeRpc(final YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+ String netconfSessionIdForReporting) {
+ super(configRegistryClient, netconfSessionIdForReporting);
+ this.yangStoreSnapshot = yangStoreSnapshot;
+ }
+
+ private String getStringRepresentation(final Object result) {
+ final SimpleType<?> simpleType = SimpleTypeResolver.getSimpleType(result.getClass().getName());
+ final Optional<String> mappedAttributeOpt = new SimpleAttributeMappingStrategy(simpleType).mapAttribute(result);
+ return mappedAttributeOpt.isPresent() ? mappedAttributeOpt.get() : "";
+ }
+
+ private Object executeOperation(final ConfigRegistryClient configRegistryClient, final ObjectName on,
+ final String name, final Map<String, AttributeConfigElement> attributes) {
+ final Object[] params = new Object[attributes.size()];
+ final String[] signature = new String[attributes.size()];
+
+ int i = 0;
+ for (final String attrName : attributes.keySet()) {
+ final AttributeConfigElement attribute = attributes.get(attrName);
+ final Optional<?> resolvedValueOpt = attribute.getResolvedValue();
+
+ params[i] = resolvedValueOpt.isPresent() ? resolvedValueOpt.get() : attribute.getResolvedDefaultValue();
+ signature[i] = resolvedValueOpt.isPresent() ? resolvedValueOpt.get().getClass().getName() : attribute
+ .getResolvedDefaultValue().getClass().getName();
+ i++;
+ }
+
+ return configRegistryClient.invokeMethod(on, name, params, signature);
+ }
+
+ public NetconfOperationExecution fromXml(final XmlElement xml) throws NetconfDocumentedException {
+ final String namespace = xml.getNamespace();
+ final XmlElement contextInstanceElement = xml.getOnlyChildElement(CONTEXT_INSTANCE);
+ final String operationName = xml.getName();
+
+ final RuntimeRpcElementResolved id = RuntimeRpcElementResolved.fromXpath(
+ contextInstanceElement.getTextContent(), operationName, namespace);
+
+ final Rpcs rpcs = mapRpcs(yangStoreSnapshot.getModuleMXBeanEntryMap());
+
+ final ModuleRpcs rpcMapping = rpcs.getRpcMapping(id);
+ final InstanceRuntimeRpc instanceRuntimeRpc = rpcMapping.getRpc(id.getRuntimeBeanName(), operationName);
+
+ // TODO move to Rpcs after xpath attribute is redesigned
+
+ final ObjectName on = id.getObjectName(rpcMapping);
+ Map<String, AttributeConfigElement> attributes = instanceRuntimeRpc.fromXml(xml);
+ attributes = sortAttributes(attributes, xml);
+
+ return new NetconfOperationExecution(on, instanceRuntimeRpc.getName(), attributes,
+ instanceRuntimeRpc.getReturnType(), namespace);
+ }
+
+ @Override
+ public HandlingPriority canHandle(Document message) {
+ XmlElement requestElement = getRequestElementWithCheck(message);
+
+ XmlElement operationElement = requestElement.getOnlyChildElement();
+ final String netconfOperationName = operationElement.getName();
+ final String netconfOperationNamespace = operationElement.getNamespace();
+
+ final Optional<XmlElement> contextInstanceElement = operationElement
+ .getOnlyChildElementOptionally(CONTEXT_INSTANCE);
+
+ if (contextInstanceElement.isPresent() == false)
+ return HandlingPriority.CANNOT_HANDLE;
+
+ final RuntimeRpcElementResolved id = RuntimeRpcElementResolved.fromXpath(contextInstanceElement.get()
+ .getTextContent(), netconfOperationName, netconfOperationNamespace);
+
+ // TODO reuse rpcs instance in fromXml method
+ final Rpcs rpcs = mapRpcs(yangStoreSnapshot.getModuleMXBeanEntryMap());
+
+ try {
+
+ final ModuleRpcs rpcMapping = rpcs.getRpcMapping(id);
+ final InstanceRuntimeRpc instanceRuntimeRpc = rpcMapping.getRpc(id.getRuntimeBeanName(),
+ netconfOperationName);
+ Preconditions.checkState(instanceRuntimeRpc != null, "No rpc found for %s:%s", netconfOperationNamespace,
+ netconfOperationName);
+
+ } catch (IllegalStateException e) {
+ logger.debug("Cannot handle runtime operation {}:{}", netconfOperationNamespace, netconfOperationName, e);
+ return HandlingPriority.CANNOT_HANDLE;
+ }
+
+ return HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY;
+ }
+
+ @Override
+ protected HandlingPriority canHandle(String netconfOperationName, String namespace) {
+ throw new UnsupportedOperationException(
+ "This should not be used since it is not possible to provide check with these attributes");
+ }
+
+ @Override
+ protected String getOperationName() {
+ throw new UnsupportedOperationException("Runtime rpc does not have a stable name");
+ }
+
+ @Override
+ protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+
+ // TODO exception handling
+ // TODO check for namespaces and unknown elements
+
+ final NetconfOperationExecution execution = fromXml(xml);
+
+ logger.debug("Invoking operation {} on {} with arguments {}", execution.operationName, execution.on,
+ execution.attributes);
+ final Object result = executeOperation(configRegistryClient, execution.on, execution.operationName,
+ execution.attributes);
+
+ logger.info("Operation {} called successfully on {} with arguments {} with result {}", execution.operationName,
+ execution.on, execution.attributes, result);
+
+ if (execution.returnType.equals("void")) {
+ return document.createElement("ok");
+ } else {
+ final Element output = XmlUtil.createTextElement(document, "result", getStringRepresentation(result));
+ XmlUtil.addNamespaceAttr(output, execution.namespace);
+ return output;
+ }
+ }
+
+ private static class NetconfOperationExecution {
+
+ private final ObjectName on;
+ private final String operationName;
+ private final Map<String, AttributeConfigElement> attributes;
+ private final String returnType;
+ private final String namespace;
+
+ public NetconfOperationExecution(final ObjectName on, final String name,
+ final Map<String, AttributeConfigElement> attributes, final String returnType, final String namespace) {
+ this.on = on;
+ this.operationName = name;
+ this.attributes = attributes;
+ this.returnType = returnType;
+ this.namespace = namespace;
+ }
+
+ }
+
+ private static Map<String, AttributeConfigElement> sortAttributes(
+ final Map<String, AttributeConfigElement> attributes, final XmlElement xml) {
+ final Map<String, AttributeConfigElement> sorted = Maps.newLinkedHashMap();
+
+ for (XmlElement xmlElement : xml.getChildElements()) {
+ final String name = xmlElement.getName();
+ if (CONTEXT_INSTANCE.equals(name) == false) { // skip context
+ // instance child node
+ // because it
+ // specifies
+ // ObjectName
+ final AttributeConfigElement value = attributes.get(name);
+ if (value == null) {
+ throw new IllegalArgumentException("Cannot find yang mapping for node " + xmlElement);
+ }
+ sorted.put(name, value);
+ }
+ }
+
+ return sorted;
+ }
+
+ private static Rpcs mapRpcs(final Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+
+ final Map<String, Map<String, ModuleRpcs>> map = Maps.newHashMap();
+
+ for (final String namespace : mBeanEntries.keySet()) {
+
+ Map<String, ModuleRpcs> namespaceToModules = map.get(namespace);
+ if (namespaceToModules == null) {
+ namespaceToModules = Maps.newHashMap();
+ map.put(namespace, namespaceToModules);
+ }
+
+ for (final String moduleName : mBeanEntries.get(namespace).keySet()) {
+
+ ModuleRpcs rpcMapping = namespaceToModules.get(moduleName);
+ if (rpcMapping == null) {
+ rpcMapping = new ModuleRpcs();
+ namespaceToModules.put(moduleName, rpcMapping);
+ }
+
+ final ModuleMXBeanEntry entry = mBeanEntries.get(namespace).get(moduleName);
+
+ for (final RuntimeBeanEntry runtimeEntry : entry.getRuntimeBeans()) {
+ rpcMapping.addNameMapping(runtimeEntry);
+ for (final Rpc rpc : runtimeEntry.getRpcs()) {
+ rpcMapping.addRpc(runtimeEntry, rpc);
+ }
+ }
+ }
+ }
+
+ return new Rpcs(map);
+ }
+
+}
--- /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.operations.runtimerpc;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+
+import javax.management.ObjectName;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Represents parsed xpath to runtime bean instance
+ */
+public final class RuntimeRpcElementResolved {
+ private final String moduleName;
+ private final String instanceName;
+ private final String namespace;
+ private final String runtimeBeanName;
+ private final Map<String, String> additionalAttributes;
+
+ private RuntimeRpcElementResolved(String namespace, String moduleName, String instanceName, String runtimeBeanName,
+ Map<String, String> additionalAttributes) {
+ this.moduleName = moduleName;
+ this.instanceName = instanceName;
+ this.additionalAttributes = additionalAttributes;
+ this.namespace = namespace;
+ this.runtimeBeanName = runtimeBeanName;
+ }
+
+ public String getModuleName() {
+ return moduleName;
+ }
+
+ public String getInstanceName() {
+ return instanceName;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public String getRuntimeBeanName() {
+ return runtimeBeanName;
+ }
+
+ public ObjectName getObjectName(ModuleRpcs rpcMapping) {
+ Map<String, String> additionalAttributesJavaNames = Maps
+ .newHashMapWithExpectedSize(additionalAttributes.size());
+ for (String attributeYangName : additionalAttributes.keySet()) {
+ String attributeJavaName = rpcMapping.getRbeJavaName(attributeYangName);
+ Preconditions.checkState(attributeJavaName != null,
+ "Cannot find java name for runtime bean wtih yang name %s", attributeYangName);
+ additionalAttributesJavaNames.put(attributeJavaName, additionalAttributes.get(attributeYangName));
+ }
+ return ObjectNameUtil.createRuntimeBeanName(moduleName, instanceName, additionalAttributesJavaNames);
+ }
+
+ private static final String xpathPatternBlueprint = "/" + XmlNetconfConstants.DATA_KEY + "/"
+ + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\["
+ + XmlNetconfConstants.NAME_KEY + "='(.+)'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\["
+ + XmlNetconfConstants.NAME_KEY + "='([^']+)'\\](.*)";
+ private static final Pattern xpathPattern = Pattern.compile(xpathPatternBlueprint);
+ private static final String additionalPatternBlueprint = "(.+)\\[(.+)='(.+)'\\]";
+ private static final Pattern additionalPattern = Pattern.compile(additionalPatternBlueprint);
+
+ public static RuntimeRpcElementResolved fromXpath(String xpath, String elementName, String namespace) {
+ Matcher matcher = xpathPattern.matcher(xpath);
+ Preconditions.checkState(matcher.matches(),
+ "Node %s with value '%s' not in required form on rpc element %s, required format is %s",
+ RuntimeRpc.CONTEXT_INSTANCE, xpath, elementName, xpathPatternBlueprint);
+
+ String moduleName = matcher.group(1);
+ String instanceName = matcher.group(2);
+ String additionalString = matcher.group(3);
+ HashMap<String, String> additionalAttributes = Maps.<String, String> newHashMap();
+ String runtimeBeanYangName = moduleName;
+ for (String additionalKeyValue : additionalString.split("/")) {
+ if (Strings.isNullOrEmpty(additionalKeyValue))
+ continue;
+ matcher = additionalPattern.matcher(additionalKeyValue);
+ Preconditions
+ .checkState(
+ matcher.matches(),
+ "Attribute %s not in required form on rpc element %s, required format for additional attributes is %s",
+ additionalKeyValue, elementName, additionalPatternBlueprint);
+ String name = matcher.group(1);
+ runtimeBeanYangName = name;
+ additionalAttributes.put(name, matcher.group(3));
+ }
+
+ return new RuntimeRpcElementResolved(namespace, moduleName, instanceName, runtimeBeanYangName,
+ additionalAttributes);
+ }
+}
--- /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.osgi;
+
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Hashtable;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class Activator implements BundleActivator, YangStoreServiceTracker.YangStoreTrackerListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(Activator.class);
+
+ private BundleContext context;
+ ServiceRegistration osgiRegistration;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ this.context = context;
+ YangStoreServiceTracker tracker = new YangStoreServiceTracker(context, this);
+ tracker.open();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ }
+
+ @Override
+ public synchronized void onYangStoreAdded(YangStoreService yangStoreService) {
+ checkState(osgiRegistration == null, "More than one onYangStoreAdded received");
+ NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
+ logger.debug("Registering into OSGi");
+ osgiRegistration = context.registerService(new String[]{NetconfOperationServiceFactory.class.getName()}, factory,
+ new Hashtable<String, Object>());
+ }
+
+ @Override
+ public synchronized void onYangStoreRemoved() {
+ osgiRegistration.unregister();
+ osgiRegistration = null;
+ }
+}
--- /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.osgi;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Validate;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpc;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+
+import java.util.Set;
+
+final class NetconfOperationProvider {
+ private final YangStoreSnapshot yangStoreSnapshot;
+ private final Set<NetconfOperation> operations;
+ private final ConfigRegistryClient configRegistryClient;
+ private final TransactionProvider transactionProvider;
+
+ NetconfOperationProvider(YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+ TransactionProvider transactionProvider, String netconfSessionIdForReporting) {
+
+ this.yangStoreSnapshot = yangStoreSnapshot;
+ this.configRegistryClient = configRegistryClient;
+ this.transactionProvider = transactionProvider;
+ operations = setUpOperations(yangStoreSnapshot, configRegistryClient, transactionProvider,
+ netconfSessionIdForReporting);
+ }
+
+ Set<NetconfOperation> getOperations() {
+ return operations;
+ }
+
+ private static Set<NetconfOperation> setUpOperations(YangStoreSnapshot yangStoreSnapshot,
+ ConfigRegistryClient configRegistryClient, TransactionProvider transactionProvider,
+ String netconfSessionIdForReporting) {
+ Set<NetconfOperation> ops = Sets.newHashSet();
+
+ GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional.<String> absent(), transactionProvider,
+ configRegistryClient, netconfSessionIdForReporting);
+
+ ops.add(getConfigOp);
+ ops.add(new EditConfig(yangStoreSnapshot, transactionProvider, configRegistryClient,
+ netconfSessionIdForReporting));
+ ops.add(new Commit(transactionProvider, configRegistryClient, netconfSessionIdForReporting));
+ ops.add(new Get(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting));
+ ops.add(new DiscardChanges(transactionProvider, configRegistryClient, netconfSessionIdForReporting));
+ ops.add(new Validate(transactionProvider, configRegistryClient, netconfSessionIdForReporting));
+ ops.add(new RuntimeRpc(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting));
+
+ return ops;
+ }
+
+}
--- /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.osgi;
+
+import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.MBeanServer;
+import java.lang.management.ManagementFactory;
+
+public class NetconfOperationServiceFactoryImpl implements NetconfOperationServiceFactory {
+
+ public static final int ATTEMPT_TIMEOUT_MS = 1000;
+
+ private final YangStoreService yangStoreService;
+ private final ConfigRegistryJMXClient jmxClient;
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceFactoryImpl.class);
+
+ public NetconfOperationServiceFactoryImpl(YangStoreService yangStoreService) {
+ this(yangStoreService, ManagementFactory.getPlatformMBeanServer());
+ }
+
+ public NetconfOperationServiceFactoryImpl(YangStoreService yangStoreService, MBeanServer mBeanServer) {
+ this.yangStoreService = yangStoreService;
+
+ // Config registry might not be present yet, but will be eventually
+ while(true) {
+
+ final ConfigRegistryJMXClient configRegistryJMXClient;
+ try {
+ configRegistryJMXClient = new ConfigRegistryJMXClient(mBeanServer);
+ } catch (IllegalStateException e) {
+ logger.debug("Jmx client could not be created, reattempting");
+ try {
+ Thread.sleep(ATTEMPT_TIMEOUT_MS);
+ } catch (InterruptedException e1) {
+ throw new RuntimeException(e1);
+ }
+ continue;
+ }
+
+ jmxClient = configRegistryJMXClient;
+ break;
+ }
+ }
+
+ @Override
+ public NetconfOperationServiceImpl createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ try {
+ return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting);
+ } catch (YangStoreException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
--- /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.osgi;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages life cycle of {@link YangStoreSnapshot}.
+ */
+public class NetconfOperationServiceImpl implements NetconfOperationService {
+
+ private final YangStoreSnapshot yangStoreSnapshot;
+ private final NetconfOperationProvider operationProvider;
+ private final Set<Capability> capabilities;
+ private final TransactionProvider transactionProvider;
+
+ public NetconfOperationServiceImpl(YangStoreService yangStoreService, ConfigRegistryJMXClient jmxClient,
+ String netconfSessionIdForReporting) throws YangStoreException {
+
+ yangStoreSnapshot = yangStoreService.getYangStoreSnapshot();
+ transactionProvider = new TransactionProvider(jmxClient, netconfSessionIdForReporting);
+ operationProvider = new NetconfOperationProvider(yangStoreSnapshot, jmxClient, transactionProvider,
+ netconfSessionIdForReporting);
+ capabilities = setupCapabilities(yangStoreSnapshot);
+ }
+
+ @Override
+ public void close() {
+ yangStoreSnapshot.close();
+ transactionProvider.close();
+ }
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return capabilities;
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return operationProvider.getOperations();
+ }
+
+ @Override
+ public Set<NetconfOperationFilter> getFilters() {
+ return Collections.emptySet();
+ }
+
+ private static Set<Capability> setupCapabilities(YangStoreSnapshot yangStoreSnapshot) {
+ Set<Capability> capabilities = Sets.newHashSet();
+
+ capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
+ capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
+ capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:operations:1.0"));
+ capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:operations:1.1"));
+ capabilities
+ .add(new BasicCapability(
+ "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04"));
+
+ final Collection<Map.Entry<Module, String>> modulesAndContents = yangStoreSnapshot.getModuleMap().values();
+ for (Map.Entry<Module, String> moduleAndContent : modulesAndContents) {
+ capabilities.add(new YangStoreCapability(moduleAndContent));
+ }
+
+ return capabilities;
+ }
+
+ private static class BasicCapability implements Capability {
+
+ private final String capability;
+
+ private BasicCapability(String capability) {
+ this.capability = capability;
+ }
+
+ @Override
+ public String getCapabilityUri() {
+ return capability;
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+ }
+
+ private static class YangStoreCapability extends BasicCapability {
+
+ private final String content;
+ private final String revision;
+ private final String moduleName;
+
+ public YangStoreCapability(Map.Entry<Module, String> moduleAndContent) {
+ super(getAsString(moduleAndContent.getKey()));
+ this.content = moduleAndContent.getValue();
+ Module module = moduleAndContent.getKey();
+ this.moduleName = module.getName();
+ this.revision = Util.writeDate(module.getRevision());
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.of(content);
+ }
+
+ private static String getAsString(Module module) {
+ final StringBuffer capabilityContent = new StringBuffer();
+ capabilityContent.append(module.getNamespace());
+ capabilityContent.append("?module=");
+ capabilityContent.append(module.getName());
+ capabilityContent.append("&revision=");
+ capabilityContent.append(Util.writeDate(module.getRevision()));
+ return capabilityContent.toString();
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(moduleName);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(revision);
+ }
+ }
+}
--- /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.osgi;
+
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+class YangStoreServiceTracker extends ServiceTracker<YangStoreService, YangStoreService> {
+ private final YangStoreTrackerListener listener;
+
+ YangStoreServiceTracker(BundleContext context, final YangStoreTrackerListener listener) {
+ super(context, YangStoreService.class, null);
+ this.listener = listener;
+ }
+
+ @Override
+ public synchronized YangStoreService addingService(final ServiceReference<YangStoreService> reference) {
+ final YangStoreService yangStoreService = super.addingService(reference);
+ listener.onYangStoreAdded(yangStoreService);
+ return yangStoreService;
+ }
+
+ @Override
+ public synchronized void removedService(final ServiceReference<YangStoreService> reference,
+ final YangStoreService service) {
+ listener.onYangStoreRemoved();
+ }
+
+ static interface YangStoreTrackerListener {
+ void onYangStoreAdded(YangStoreService yangStoreService);
+ void onYangStoreRemoved();
+ }
+}
--- /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.transactions;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class TransactionProvider implements AutoCloseable {
+ private static final Logger logger = LoggerFactory.getLogger(TransactionProvider.class);
+
+ private final ConfigRegistryClient configRegistryClient;
+
+ private final String netconfSessionIdForReporting;
+ private ObjectName transaction;
+ private final List<ObjectName> allOpenedTransactions = new ArrayList<>();
+
+ public TransactionProvider(ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) {
+ this.configRegistryClient = configRegistryClient;
+ this.netconfSessionIdForReporting = netconfSessionIdForReporting;
+ }
+
+ @Override
+ public synchronized void close() {
+ for (ObjectName tx : allOpenedTransactions) {
+ if (isStillOpenTransaction(tx)) {
+ try {
+ configRegistryClient.getConfigTransactionClient(tx).abortConfig();
+ } catch (Exception e) {
+ logger.debug("Ignoring {} while closing transaction {}", e.toString(), tx, e);
+ }
+ }
+ }
+ allOpenedTransactions.clear();
+ }
+
+ public Optional<ObjectName> getTransaction() {
+
+ if (transaction == null)
+ return Optional.absent();
+
+ // Transaction was already closed somehow
+ if (isStillOpenTransaction(transaction) == false) {
+ logger.warn("Fixing illegal state: transaction {} was closed in {}", transaction,
+ netconfSessionIdForReporting);
+ transaction = null;
+ return Optional.absent();
+ }
+ return Optional.of(transaction);
+ }
+
+ private boolean isStillOpenTransaction(ObjectName transaction) {
+ boolean isStillOpenTransaction = configRegistryClient.getOpenConfigs().contains(transaction);
+ return isStillOpenTransaction;
+ }
+
+ public synchronized ObjectName getOrCreateTransaction() {
+ Optional<ObjectName> ta = getTransaction();
+
+ if (ta.isPresent())
+ return ta.get();
+ transaction = configRegistryClient.beginConfig();
+ allOpenedTransactions.add(transaction);
+ return transaction;
+ }
+
+ /**
+ * Used for editConfig test option
+ */
+ public synchronized ObjectName getTestTransaction() {
+ ObjectName testTx = configRegistryClient.beginConfig();
+ allOpenedTransactions.add(testTx);
+ return testTx;
+ }
+
+ /**
+ * Commit and notification send must be atomic
+ */
+ public synchronized CommitStatus commitTransaction() throws NetconfDocumentedException {
+ final Optional<ObjectName> taON = getTransaction();
+ Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting);
+ CommitStatus status = configRegistryClient.commitConfig(taON.get());
+ allOpenedTransactions.remove(transaction);
+ transaction = null;
+ return status;
+ }
+
+ public synchronized void abortTransaction() {
+ Optional<ObjectName> taON = getTransaction();
+ Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting);
+
+ ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
+ transactionClient.abortConfig();
+ allOpenedTransactions.remove(transaction);
+ transaction = null;
+ }
+
+ public synchronized void abortTestTransaction(ObjectName testTx) {
+ ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(testTx);
+ allOpenedTransactions.remove(testTx);
+ transactionClient.abortConfig();
+ }
+
+ public void validateTransaction() throws ValidationException {
+ Optional<ObjectName> taON = getTransaction();
+ Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting);
+
+ ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
+ transactionClient.validateConfig();
+ }
+
+ public void validateTestTransaction(ObjectName taON) {
+ ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
+ transactionClient.validateConfig();
+ }
+
+ public void wipeTestTransaction(ObjectName taON) {
+ wipeInternal(taON, true, null);
+ }
+
+ /**
+ * Wiping means removing all module instances keeping the transaction open.
+ */
+ synchronized void wipeInternal(ObjectName taON, boolean isTest, String moduleName) {
+ ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
+
+ Set<ObjectName> lookupConfigBeans = moduleName == null ? transactionClient.lookupConfigBeans()
+ : transactionClient.lookupConfigBeans(moduleName);
+ for (ObjectName instance : lookupConfigBeans) {
+ try {
+ transactionClient.destroyModule(instance);
+ } catch (InstanceNotFoundException e) {
+ if (isTest)
+ logger.debug("Unable to clean configuration in transactiom {}", taON, e);
+ else
+ logger.warn("Unable to clean configuration in transactiom {}", taON, e);
+
+ throw new IllegalStateException("Unable to clean configuration in transactiom " + taON, e);
+ }
+ }
+ logger.debug("Transaction {} wiped clean", taON);
+ }
+
+ public void wipeTransaction() {
+ Optional<ObjectName> taON = getTransaction();
+ Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting);
+ wipeInternal(taON.get(), false, null);
+ }
+
+}
--- /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.util;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public final class Util {
+
+ /**
+ * Used for date <-> xml serialization
+ */
+ private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+
+ public static String writeDate(final Date date) {
+ return dateFormat.format(date);
+ }
+
+ public static Date readDate(final String s) throws ParseException {
+ return dateFormat.parse(s);
+ }
+
+ public static void checkType(final Object value, final Class<?> clazz) {
+ Preconditions.checkArgument(clazz.isAssignableFrom(value.getClass()), "Unexpected type " + value.getClass()
+ + " should be " + clazz);
+ }
+
+ // TODO: add message and proper error types
+ public static YangStoreSnapshot getYangStore(final YangStoreService yangStoreService)
+ throws NetconfDocumentedException {
+ try {
+ return yangStoreService.getYangStoreSnapshot();
+ } catch (final YangStoreException e) {
+ throw new NetconfDocumentedException("TODO", e, ErrorType.application, ErrorTag.bad_attribute,
+ ErrorSeverity.error);
+ }
+ }
+
+}
--- /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;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+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.*;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpc;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.*;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+public class NetconfMappingTest extends AbstractConfigTest {
+ private static final Logger logger = LoggerFactory.getLogger(NetconfMappingTest.class);
+
+ private static final String INSTANCE_NAME = "test1";
+ private static final String NETCONF_SESSION_ID = "foo";
+ private NetconfTestImplModuleFactory factory;
+ private DepTestImplModuleFactory factory2;
+
+ @Mock
+ YangStoreSnapshot yangStoreSnapshot;
+ @Mock
+ NetconfOperationRouter netconfOperationRouter;
+
+ private TransactionProvider transactionProvider;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doReturn(getMbes()).when(this.yangStoreSnapshot).getModuleMXBeanEntryMap();
+ this.factory = new NetconfTestImplModuleFactory();
+ this.factory2 = new DepTestImplModuleFactory();
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(this.factory, this.factory2));
+
+ transactionProvider = new TransactionProvider(this.configRegistryClient, NETCONF_SESSION_ID);
+ }
+
+ private ObjectName createModule(final String instanceName) throws InstanceAlreadyExistsException {
+ final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
+
+ final ObjectName on = transaction.createModule(this.factory.getImplementationName(), instanceName);
+ final NetconfTestImplModuleMXBean mxBean = transaction.newMXBeanProxy(on, NetconfTestImplModuleMXBean.class);
+ setModule(mxBean, transaction);
+
+ transaction.commit();
+ return on;
+ }
+
+ @Test
+ public void testConfigNetconf() throws Exception {
+
+ createModule(INSTANCE_NAME);
+
+ edit("netconfMessages/editConfig.xml");
+ checkBinaryLeafEdited(getConfigCandidate());
+
+ // default-operation:none, should not affect binary leaf
+ edit("netconfMessages/editConfig_none.xml");
+ checkBinaryLeafEdited(getConfigCandidate());
+
+ // check after edit
+ commit();
+ Element response = getConfigRunning();
+ // System.out.println(Xml.toString(response));
+
+ checkBinaryLeafEdited(response);
+ checkTypeConfigAttribute(response);
+
+ edit("netconfMessages/editConfig_remove.xml");
+
+ commit();
+ response = getConfigCandidate();
+ final String responseFromCandidate = XmlUtil.toString(response).replaceAll("\\s+", "");
+ // System.out.println(responseFromCandidate);
+ response = getConfigRunning();
+ final String responseFromRunning = XmlUtil.toString(response).replaceAll("\\s+", "");
+ // System.out.println(responseFromRunning);
+ assertEquals(responseFromCandidate, responseFromRunning);
+
+ final String expectedResult = XmlFileLoader.fileToString("netconfMessages/editConfig_expectedResult.xml")
+ .replaceAll("\\s+", "");
+
+ assertEquals(expectedResult, responseFromRunning);
+ assertEquals(expectedResult, responseFromCandidate);
+
+ edit("netconfMessages/editConfig_none.xml");
+ doNothing().when(netconfOperationRouter).close();
+ closeSession();
+ verify(netconfOperationRouter).close();
+ verifyNoMoreInteractions(netconfOperationRouter);
+ }
+
+ private void closeSession() throws NetconfDocumentedException, ParserConfigurationException, SAXException,
+ IOException {
+ DefaultCloseSession closeOp = new DefaultCloseSession(NETCONF_SESSION_ID);
+ executeOp(closeOp, "netconfMessages/closeSession.xml");
+ }
+
+ private void edit(String resource) throws ParserConfigurationException, SAXException, IOException,
+ NetconfDocumentedException {
+ EditConfig editOp = new EditConfig(yangStoreSnapshot, transactionProvider, configRegistryClient,
+ NETCONF_SESSION_ID);
+ executeOp(editOp, resource);
+ }
+
+ private void commit() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException {
+ Commit commitOp = new Commit(transactionProvider, configRegistryClient, NETCONF_SESSION_ID);
+ executeOp(commitOp, "netconfMessages/commit.xml");
+ }
+
+ private Element getConfigCandidate() throws ParserConfigurationException, SAXException, IOException,
+ NetconfDocumentedException {
+ GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional.<String> absent(), transactionProvider,
+ configRegistryClient, NETCONF_SESSION_ID);
+ return executeOp(getConfigOp, "netconfMessages/getConfig_candidate.xml");
+ }
+
+ private Element getConfigRunning() throws ParserConfigurationException, SAXException, IOException,
+ NetconfDocumentedException {
+ GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional.<String> absent(), transactionProvider,
+ configRegistryClient, NETCONF_SESSION_ID);
+ return executeOp(getConfigOp, "netconfMessages/getConfig.xml");
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testConfigNetconfReplaceDefaultEx() throws Exception {
+
+ createModule(INSTANCE_NAME);
+
+ edit("netconfMessages/editConfig.xml");
+ edit("netconfMessages/editConfig_replace_default_ex.xml");
+ }
+
+ @Test
+ public void testConfigNetconfReplaceDefault() throws Exception {
+
+ createModule(INSTANCE_NAME);
+
+ edit("netconfMessages/editConfig.xml");
+ commit();
+ Element response = getConfigRunning();
+ final int allInstances = response.getElementsByTagName("module").getLength();
+
+ edit("netconfMessages/editConfig_replace_default.xml");
+
+ commit();
+ response = getConfigRunning();
+
+ final int afterReplace = response.getElementsByTagName("module").getLength();
+ assertEquals(4, allInstances);
+ assertEquals(2, afterReplace);
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testSameAttrDifferentNamespaces() throws Exception {
+ try {
+ edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml");
+ } catch (NetconfDocumentedException e) {
+ String message = e.getMessage();
+ assertThat(message,
+ JUnitMatchers
+ .containsString("Element simple-long-2 present multiple times with different namespaces"));
+ assertThat(message,
+ JUnitMatchers.containsString("urn:opendaylight:params:xml:ns:yang:controller:test:impl"));
+ assertThat(message,
+ JUnitMatchers
+ .containsString(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG));
+ throw e;
+ }
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testDifferentNamespaceInTO() throws Exception {
+ try {
+ edit("netconfMessages/namespaces/editConfig_differentNamespaceTO.xml");
+ } catch (NetconfDocumentedException e) {
+ String message = e.getMessage();
+ assertThat(message, JUnitMatchers.containsString("Unrecognised elements"));
+ assertThat(message, JUnitMatchers.containsString("simple-int2"));
+ assertThat(message, JUnitMatchers.containsString("dto_d"));
+ throw e;
+ }
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testSameAttrDifferentNamespacesList() throws Exception {
+ try {
+ edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml");
+ } catch (NetconfDocumentedException e) {
+ String message = e.getMessage();
+ assertThat(message,
+ JUnitMatchers.containsString("Element binaryLeaf present multiple times with different namespaces"));
+ assertThat(message,
+ JUnitMatchers.containsString("urn:opendaylight:params:xml:ns:yang:controller:test:impl"));
+ assertThat(message,
+ JUnitMatchers
+ .containsString(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG));
+ throw e;
+ }
+ }
+
+ @Test
+ public void testTypeNameConfigAttributeMatching() throws Exception {
+ edit("netconfMessages/editConfig.xml");
+ commit();
+ edit("netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml");
+ commit();
+
+ Element response = getConfigRunning();
+ checkTypeConfigAttribute(response);
+ }
+
+ // TODO add <modules operation="replace"> functionality
+ @Test(expected = NetconfDocumentedException.class)
+ public void testConfigNetconfReplaceModuleEx() throws Exception {
+
+ createModule(INSTANCE_NAME);
+
+ edit("netconfMessages/editConfig.xml");
+ edit("netconfMessages/editConfig_replace_module_ex.xml");
+ }
+
+ @Test
+ public void testUnrecognisedConfigElements() throws Exception {
+
+ String format = "netconfMessages/unrecognised/editConfig_unrecognised%d.xml";
+ final int TESTS_COUNT = 8;
+
+ for (int i = 0; i < TESTS_COUNT; i++) {
+ String file = String.format(format, i + 1);
+ try {
+ edit(file);
+ } catch (NetconfDocumentedException e) {
+ Assert.assertThat(e.getMessage(), JUnitMatchers.containsString("Unrecognised elements"));
+ Assert.assertThat(e.getMessage(), JUnitMatchers.containsString("unknownAttribute"));
+ continue;
+ }
+ fail("Unrecognised test should throw exception " + file);
+ }
+ }
+
+ @Test
+ @Ignore
+ // FIXME
+ public void testConfigNetconfReplaceModule() throws Exception {
+
+ createModule(INSTANCE_NAME);
+
+ edit("netconfMessages/editConfig.xml");
+ commit();
+ Element response = getConfigRunning();
+ final int allInstances = response.getElementsByTagName("instance").getLength();
+
+ edit("netconfMessages/editConfig_replace_module.xml");
+
+ commit();
+ response = getConfigRunning();
+ final int afterReplace = response.getElementsByTagName("instance").getLength();
+
+ Assert.assertEquals(4 + 4 /* Instances from services */, allInstances);
+ Assert.assertEquals(3 + 3, afterReplace);
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testEx() throws Exception {
+
+ commit();
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testEx2() throws Exception {
+ discard();
+ }
+
+ private void discard() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException {
+ DiscardChanges discardOp = new DiscardChanges(transactionProvider, configRegistryClient, NETCONF_SESSION_ID);
+ executeOp(discardOp, "netconfMessages/discardChanges.xml");
+ }
+
+ private void checkBinaryLeafEdited(final Element response) {
+ final NodeList children = response.getElementsByTagName("binaryLeaf");
+ assertEquals(3, children.getLength());
+ final StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < 3; i++) {
+ final Element e = (Element) children.item(i);
+ buf.append(XmlElement.fromDomElement(e).getTextContent());
+ }
+ assertEquals("810", buf.toString());
+
+ }
+
+ private void checkTypeConfigAttribute(Element response) {
+
+ XmlElement modulesElement = XmlElement.fromDomElement(response).getOnlyChildElement("data")
+ .getOnlyChildElement("modules");
+
+ XmlElement configAttributeType = null;
+ for (XmlElement moduleElement : modulesElement.getChildElements("module")) {
+ for (XmlElement type : moduleElement.getChildElements("type")) {
+ if (type.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY).equals("") == false) {
+ configAttributeType = type;
+ }
+ }
+ }
+
+ assertEquals("configAttributeType", configAttributeType.getTextContent());
+ }
+
+ private Map<String, Map<String, ModuleMXBeanEntry>> getMbes() throws Exception {
+ final List<InputStream> yangDependencies = getYangs();
+
+ final Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries = Maps.newHashMap();
+ mBeanEntries.putAll(new MbeParser().parseYangFiles(yangDependencies).getModuleMXBeanEntryMap());
+
+ return mBeanEntries;
+ }
+
+ @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);
+
+ Element response = get();
+
+ assertEquals(2, getElementsSize(response, "instance"));
+ 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(4, getElementsSize(response, "inner-running-data-additional"));
+ assertEquals(4, getElementsSize(response, "deep4"));
+ // TODO assert keys
+
+ RuntimeRpc netconf = new RuntimeRpc(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID);
+
+ response = executeOp(netconf, "netconfMessages/rpc.xml");
+ assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("testarg1".toUpperCase()));
+
+ response = executeOp(netconf, "netconfMessages/rpcInner.xml");
+ assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("ok"));
+
+ response = executeOp(netconf, "netconfMessages/rpcInnerInner.xml");
+ assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("true"));
+ }
+
+ private Element get() throws NetconfDocumentedException, ParserConfigurationException, SAXException, IOException {
+ Get getOp = new Get(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID);
+ return executeOp(getOp, "netconfMessages/get.xml");
+ }
+
+ private int getElementsSize(Element response, String elementName) {
+ 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 Boolean noArgInnerInner(Integer integer, Boolean aBoolean) {
+ return aBoolean;
+ }
+
+ }
+
+ 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 {
+
+ final Document request = XmlFileLoader.xmlFileToDocument(filename);
+
+ logger.debug("Executing netconf operation\n{}", XmlUtil.toString(request));
+ HandlingPriority priority = op.canHandle(request);
+
+ Preconditions.checkState(priority != HandlingPriority.CANNOT_HANDLE);
+
+ final Document response = op.handle(request, netconfOperationRouter);
+ logger.debug("Got response\n{}", XmlUtil.toString(response));
+ return response.getDocumentElement();
+ }
+
+ 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/ietf-inet-types.yang");
+ final Collection<InputStream> yangDependencies = new ArrayList<>();
+ for (String path : paths) {
+ final InputStream is = Preconditions
+ .checkNotNull(getClass().getResourceAsStream(path), path + " not found");
+ yangDependencies.add(is);
+ }
+ return Lists.newArrayList(yangDependencies);
+ }
+
+ private void setModule(final NetconfTestImplModuleMXBean mxBean, final ConfigTransactionJMXClient transaction)
+ throws InstanceAlreadyExistsException {
+ mxBean.setSimpleInt((long) 44);
+ mxBean.setBinaryLeaf(new byte[] { 8, 7, 9 });
+ final DtoD dtob = getDtoD();
+ mxBean.setDtoD(dtob);
+ //
+ final DtoC dtoa = getDtoC();
+ mxBean.setDtoC(dtoa);
+ mxBean.setSimpleBoolean(false);
+ //
+ final Peers p1 = new Peers();
+ p1.setCoreSize(44L);
+ p1.setPort("port1");
+ p1.setSimpleInt3(456);
+ final Peers p2 = new Peers();
+ p2.setCoreSize(44L);
+ p2.setPort("port23");
+ p2.setSimpleInt3(456);
+ mxBean.setPeers(Lists.<Peers> newArrayList(p1, p2));
+ // //
+ mxBean.setSimpleLong(454545L);
+ mxBean.setSimpleLong2(44L);
+ mxBean.setSimpleBigInteger(BigInteger.valueOf(999L));
+ mxBean.setSimpleByte(new Byte((byte) 4));
+ mxBean.setSimpleShort(new Short((short) 4));
+ mxBean.setSimpleTest(545);
+
+ mxBean.setComplexList(Lists.<ComplexList> newArrayList());
+ mxBean.setSimpleList(Lists.<Integer> newArrayList());
+
+ final ObjectName testingDepOn = transaction.createModule(this.factory2.getImplementationName(), "dep");
+ mxBean.setTestingDep(testingDepOn);
+ }
+
+ private static DtoD getDtoD() {
+ final DtoD dtob = new DtoD();
+ dtob.setSimpleInt1((long) 444);
+ dtob.setSimpleInt2((long) 4444);
+ dtob.setSimpleInt3(454);
+ final ComplexDtoBInner dtobInner = new ComplexDtoBInner();
+ final Deep deep = new Deep();
+ deep.setSimpleInt3(4);
+ dtobInner.setDeep(deep);
+ dtobInner.setSimpleInt3(44);
+ dtobInner.setSimpleList(Lists.newArrayList(4));
+ dtob.setComplexDtoBInner(Lists.newArrayList(dtobInner));
+ dtob.setSimpleList(Lists.newArrayList(4));
+ return dtob;
+ }
+
+ private static DtoC getDtoC() {
+ final DtoC dtoa = new DtoC();
+ // dtoa.setSimpleArg((long) 55);
+ final DtoAInner dtoAInner = new DtoAInner();
+ final DtoAInnerInner dtoAInnerInner = new DtoAInnerInner();
+ dtoAInnerInner.setSimpleArg(456L);
+ dtoAInner.setDtoAInnerInner(dtoAInnerInner);
+ dtoAInner.setSimpleArg(44L);
+ dtoa.setDtoAInner(dtoAInner);
+ return dtoa;
+ }
+
+}
--- /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;
+
+import org.junit.Test;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services.ServiceInstance;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ServiceTrackerTest {
+
+ @Test
+ public void test() {
+ Services.ServiceInstance serviceInstance = new ServiceInstance("module", "serviceInstance");
+
+ String string = serviceInstance.toString();
+
+ Services.ServiceInstance serviceInstance2 = Services.ServiceInstance.fromString(string);
+
+ assertEquals(serviceInstance, serviceInstance2);
+ }
+
+ @Test
+ public void testOneInstanceMultipleServices() {
+ Services services = new Services();
+ services.addServiceEntry("s1", "module", "instance");
+ assertEquals(1, services.getMappedServices().size());
+
+ services.addServiceEntry("s2", "module", "instance");
+ assertEquals(2, services.getMappedServices().size());
+ }
+
+ @Test
+ public void testMultipleInstancesOneName() throws Exception {
+ Services services = new Services();
+ services.addServiceEntry("s1", "module", "instance");
+ assertEquals(1, services.getMappedServices().size());
+
+ services.addServiceEntry("s1", "module2", "instance");
+ assertEquals(1, services.getMappedServices().size());
+ assertEquals(2, services.getMappedServices().get("s1").size());
+ assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance"));
+ assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance_1"));
+ }
+
+ @Test
+ public void testMultipleInstancesOneName2() throws Exception {
+ Services services = new Services();
+ services.addServiceEntry("s1", "module", "instance_1");
+
+ services.addServiceEntry("s2", "module2", "instance");
+ services.addServiceEntry("s2", "module3", "instance");
+ services.addServiceEntry("s1", "module3", "instance");
+
+ assertEquals(2, services.getMappedServices().get("s1").size());
+ assertEquals(2, services.getMappedServices().get("s2").size());
+ assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance_2"));
+ assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance_1"));
+ assertTrue(services.getMappedServices().get("s2").containsKey("ref_instance"));
+ assertTrue(services.getMappedServices().get("s2").containsKey("ref_instance_2"));
+ }
+
+}
--- /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.operations;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Element;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.*;
+
+public class ValidateTest {
+
+ public static final String NETCONF_SESSION_ID_FOR_REPORTING = "foo";
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void test() throws Exception {
+ final XmlElement xml = XmlElement.fromString("<abc></abc>");
+ final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING);
+ validate.handle(null, xml);
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testNoSource() throws Exception {
+ final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+ + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0 + "\"/>");
+ final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING);
+ validate.handle(null, xml);
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testNoNamespace() throws Exception {
+ final XmlElement xml = XmlElement.fromString("<validate/>");
+ final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING);
+ validate.handle(null, xml);
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testRunningSource() throws Exception {
+
+ final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+ + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0
+ + "\"><source><running></running></source></validate>");
+ final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING);
+ validate.handle(null, xml);
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testNoTransaction() throws Exception {
+ final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+ + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0
+ + "\"><source><candidate/></source></validate>");
+ final TransactionProvider transactionProvider = mock(TransactionProvider.class);
+ doThrow(IllegalStateException.class).when(transactionProvider).validateTransaction();
+ final Validate validate = new Validate(transactionProvider, null, NETCONF_SESSION_ID_FOR_REPORTING);
+ validate.handle(null, xml);
+ }
+
+ @Test(expected = NetconfDocumentedException.class)
+ public void testValidationException() throws Exception {
+ final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+ + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0
+ + "\">><source><candidate/></source></validate>");
+ final TransactionProvider transactionProvider = mock(TransactionProvider.class);
+ doThrow(ValidationException.class).when(transactionProvider).validateTransaction();
+ final Validate validate = new Validate(transactionProvider, null, NETCONF_SESSION_ID_FOR_REPORTING);
+ validate.handle(null, xml);
+ }
+
+ @Test
+ public void testValidation() throws Exception {
+ final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+ + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0
+ + "\"><source><candidate/></source></validate>");
+ final TransactionProvider transactionProvider = mock(TransactionProvider.class);
+ final Element okElement = XmlUtil.readXmlToElement("<ok/>");
+ doNothing().when(transactionProvider).validateTransaction();
+ final Validate validate = new Validate(transactionProvider, null, NETCONF_SESSION_ID_FOR_REPORTING);
+ Element ok = validate.handle(XmlUtil.newDocument(), xml);
+ assertEquals(XmlUtil.toString(okElement), XmlUtil.toString(ok));
+ }
+
+}
--- /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.operations.editconfig;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfigElementResolved;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementResolved;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser.EditConfigExecution;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.ValidateTest;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+
+import javax.management.ObjectName;
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.*;
+
+public class EditConfigTest {
+
+ @Mock
+ private YangStoreSnapshot yangStoreSnapshot;
+ @Mock
+ private TransactionProvider provider;
+ @Mock
+ private ConfigRegistryClient configRegistry;
+ @Mock
+ private ConfigTransactionClient configTransactionClient;
+ @Mock
+ private ObjectName mockOn;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn("mockON").when(mockOn).toString();
+ doReturn(mockOn).when(provider).getTestTransaction();
+ doNothing().when(provider).validateTestTransaction(any(ObjectName.class));
+
+ doReturn(mockOn).when(provider).getTestTransaction();
+ doNothing().when(provider).abortTestTransaction(any(ObjectName.class));
+ doReturn(mockOn).when(provider).getOrCreateTransaction();
+
+ doReturn(configTransactionClient).when(configRegistry).getConfigTransactionClient(any(ObjectName.class));
+ doReturn("mockConfigTransactionClient").when(configTransactionClient).toString();
+
+ doReturn(mockOn).when(configTransactionClient).lookupConfigBean(anyString(), anyString());
+ }
+
+ @Test
+ public void test() throws NetconfDocumentedException {
+ EditConfig edit = new EditConfig(yangStoreSnapshot, provider, configRegistry,
+ ValidateTest.NETCONF_SESSION_ID_FOR_REPORTING);
+ EditConfigStrategy editStrat = mock(EditConfigStrategy.class);
+ doNothing().when(editStrat).executeConfiguration(anyString(), anyString(), anyMap(),
+ any(ConfigTransactionClient.class));
+ Map<String, Multimap<String, ModuleElementResolved>> resolvedXmlElements = getMapping(editStrat);
+
+ Config cfg = mock(Config.class);
+ XmlElement xmlElement = mock(XmlElement.class);
+ doReturn(resolvedXmlElements).when(cfg).fromXml(xmlElement);
+
+ EditConfigExecution editConfigExecution = new EditConfigExecution(null, cfg, xmlElement,
+ EditConfigXmlParser.TestOption.testThenSet);
+
+ edit.getResponseInternal(XmlUtil.newDocument(), editConfigExecution);
+
+ verify(provider).getTestTransaction();
+ verify(provider).validateTestTransaction(mockOn);
+ verify(provider).abortTestTransaction(mockOn);
+
+ verify(provider).getOrCreateTransaction();
+
+ // For every instance execute strat
+ verify(editStrat, times(2/* Test */+ 2/* Set */)).executeConfiguration(anyString(), anyString(), anyMap(),
+ any(ConfigTransactionClient.class));
+ }
+
+ private Map<String, Multimap<String, ModuleElementResolved>> getMapping(EditConfigStrategy editStrat) {
+ Map<String, Multimap<String, ModuleElementResolved>> result = Maps.newHashMap();
+
+ Multimap<String, ModuleElementResolved> innerMultimap = HashMultimap.create();
+ Map<String, AttributeConfigElement> attributes = getSimpleAttributes();
+
+ InstanceConfigElementResolved ice1 = mock(InstanceConfigElementResolved.class);
+ doReturn(attributes).when(ice1).getConfiguration();
+ doReturn(editStrat).when(ice1).getEditStrategy();
+ innerMultimap.put("m1", new ModuleElementResolved("i1", ice1));
+
+ InstanceConfigElementResolved ice2 = mock(InstanceConfigElementResolved.class);
+ doReturn(attributes).when(ice2).getConfiguration();
+ doReturn(editStrat).when(ice2).getEditStrategy();
+ innerMultimap.put("m1", new ModuleElementResolved("i2", ice2));
+
+ result.put("n1", innerMultimap);
+
+ return result;
+ }
+
+ static Map<String, AttributeConfigElement> getSimpleAttributes() {
+ Map<String, AttributeConfigElement> attributes = Maps.newHashMap();
+ AttributeConfigElement ace1 = mock(AttributeConfigElement.class);
+ doReturn("abcd").when(ace1).getResolvedDefaultValue();
+ doReturn(Optional.<String> of("abc")).when(ace1).getResolvedValue();
+ doReturn("mockedAce1").when(ace1).toString();
+ doReturn("jmxNameAce1").when(ace1).getJmxName();
+ attributes.put("a1", ace1);
+ return attributes;
+ }
+
+}
--- /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.operations.editconfig;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.*;
+
+import java.util.Map;
+
+import javax.management.Attribute;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigTest;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.ReplaceEditConfigStrategy;
+
+public class ReplaceEditConfigStrategyTest {
+
+ @Mock
+ private ConfigTransactionClient ta;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doNothing().when(ta).destroyConfigBean(anyString(), anyString());
+ doReturn(mockON()).when(ta).lookupConfigBean(anyString(), anyString());
+ doNothing().when(ta).setAttribute(any(ObjectName.class), anyString(), any(Attribute.class));
+ }
+
+ @Test
+ public void test() throws Exception {
+ ReplaceEditConfigStrategy strat = new ReplaceEditConfigStrategy();
+
+ Map<String, AttributeConfigElement> map = EditConfigTest.getSimpleAttributes();
+
+ doReturn(Sets.newHashSet(mockON(), mockON())).when(ta).lookupConfigBeans();
+
+ strat.executeConfiguration("m1", "i1", map, ta);
+
+ verify(ta).lookupConfigBean(anyString(), anyString());
+ verify(ta).setAttribute(any(ObjectName.class), anyString(), any(Attribute.class));
+ }
+
+ ObjectName mockON() {
+ ObjectName mock = mock(ObjectName.class);
+ doReturn("mockON").when(mock).toString();
+ return mock;
+ }
+
+}
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>config-persister-impl</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-api</artifactId>
+ <version>0.2.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-client</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-file-adapter</artifactId>
+ <version>0.2.1-SNAPSHOT</version>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator
+ </Bundle-Activator>
+ <Require-Capability>org.opendaylight.controller.config.persister.storage.adapter
+ </Require-Capability>
+ <Import-Package>
+ com.google.common.base,
+ com.google.common.collect,
+ javax.management,
+ javax.xml.parsers,
+ org.opendaylight.controller.config.persist.api,
+ org.opendaylight.controller.config.stat,
+ org.opendaylight.controller.config.persist.api.storage,
+ org.opendaylight.controller.netconf.api,
+ org.opendaylight.controller.netconf.api.jmx,
+ org.opendaylight.controller.netconf.client,
+ org.opendaylight.controller.netconf.util.osgi,
+ org.opendaylight.controller.netconf.util.xml,
+ org.osgi.framework,
+ org.slf4j,
+ org.w3c.dom,
+ org.xml.sax,
+ </Import-Package>
+ <Export-Package>
+ </Export-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.persist.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
+import org.opendaylight.controller.netconf.api.jmx.DefaultCommitOperationMXBean;
+import org.opendaylight.controller.netconf.api.jmx.NetconfJMXNotification;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.annotation.concurrent.ThreadSafe;
+import javax.management.*;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Responsible for listening for notifications from netconf containing latest
+ * committed configuration that should be persisted, and also for loading last
+ * configuration.
+ */
+@ThreadSafe
+public class ConfigPersisterNotificationHandler implements NotificationListener, Closeable {
+
+ private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
+
+ private final InetSocketAddress address;
+
+ private NetconfClient netconfClient;
+
+ private final Persister persister;
+ private final MBeanServerConnection mbeanServer;
+ private Long currentSessionId;
+
+ private final ObjectName on = DefaultCommitOperationMXBean.objectName;
+
+ public static final long DEFAULT_TIMEOUT = 40000L;
+ private final long timeout;
+
+ public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address,
+ MBeanServerConnection mbeanServer) {
+ this(persister, address, mbeanServer, DEFAULT_TIMEOUT);
+ }
+
+ public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address,
+ MBeanServerConnection mbeanServer, long timeout) {
+ this.persister = persister;
+ this.address = address;
+ this.mbeanServer = mbeanServer;
+ this.timeout = timeout;
+ }
+
+ public void init() throws InterruptedException {
+ Optional<Persister.ConfigSnapshotHolder> maybeConfig = loadLastConfig();
+
+ if (maybeConfig.isPresent()) {
+ logger.debug("Last config found {}", persister);
+
+ registerToNetconf(maybeConfig.get().getCapabilities());
+
+ final String configSnapshot = maybeConfig.get().getConfigSnapshot();
+ try {
+ pushLastConfig(XmlUtil.readXmlToElement(configSnapshot));
+ } catch (SAXException | IOException e) {
+ throw new IllegalStateException("Unable to load last config", e);
+ }
+
+ } else {
+ // this ensures that netconf is initialized, this is first
+ // connection
+ // this means we can register as listener for commit
+ registerToNetconf(Collections.<String>emptySet());
+
+ logger.info("No last config provided by backend storage {}", persister);
+ }
+ registerAsJMXListener();
+ }
+
+ private synchronized long registerToNetconf(Set<String> expectedCaps) throws InterruptedException {
+
+ Set<String> currentCapabilities = Sets.newHashSet();
+
+ // TODO think about moving capability subset check to netconf client
+ // could be utilized by integration tests
+
+ long pollingStart = System.currentTimeMillis();
+ int delay = 5000;
+
+ int attempt = 0;
+
+ while (true) {
+ attempt++;
+
+ try {
+ netconfClient = new NetconfClient(this.toString(), address, delay);
+ // TODO is this correct ex to catch ?
+ } catch (IllegalStateException e) {
+ logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e);
+ Thread.sleep(delay);
+ continue;
+ }
+ currentCapabilities = netconfClient.getCapabilities();
+
+ if (isSubset(currentCapabilities, expectedCaps)) {
+ logger.debug("Hello from netconf stable with {} capabilities", currentCapabilities);
+ currentSessionId = netconfClient.getSessionId();
+ logger.info("Session id received from netconf server: {}", currentSessionId);
+ return currentSessionId;
+ }
+
+ if (System.currentTimeMillis() > pollingStart + timeout) {
+ break;
+ }
+
+ logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, currentCapabilities);
+
+ try {
+ netconfClient.close();
+ } catch (IOException e) {
+ throw new RuntimeException("Error closing temporary client " + netconfClient);
+ }
+
+ Thread.sleep(delay);
+ }
+
+ throw new RuntimeException("Netconf server did not provide required capabilities " + expectedCaps
+ + " in time, provided capabilities " + currentCapabilities);
+
+ }
+
+ private boolean isSubset(Set<String> currentCapabilities, Set<String> expectedCaps) {
+ for (String exCap : expectedCaps) {
+ if (currentCapabilities.contains(exCap) == false)
+ return false;
+ }
+ return true;
+ }
+
+ private void registerAsJMXListener() {
+ try {
+ mbeanServer.addNotificationListener(on, this, null, null);
+ } catch (InstanceNotFoundException | IOException e) {
+ throw new RuntimeException("Cannot register as JMX listener to netconf", e);
+ }
+ }
+
+ @Override
+ public void handleNotification(Notification notification, Object handback) {
+ if (notification instanceof NetconfJMXNotification == false)
+ return;
+
+ // Socket should not be closed at this point
+ // Activator unregisters this as JMX listener before close is called
+
+ logger.debug("Received notification {}", notification);
+ if (notification instanceof CommitJMXNotification) {
+ try {
+ handleAfterCommitNotification((CommitJMXNotification) notification);
+ } catch (Exception e) {
+ // TODO: notificationBroadcast support logs only DEBUG
+ logger.warn("Exception occured during notification handling: ", e);
+ throw e;
+ }
+ } else
+ throw new IllegalStateException("Unknown config registry notification type " + notification);
+ }
+
+ 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();
+ }
+ });
+ 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;
+ try {
+ maybeConfigElement = persister.loadLastConfig();
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to load configuration", e);
+ }
+ return maybeConfigElement;
+ }
+
+ private synchronized void pushLastConfig(Element persistedConfig) {
+ StringBuilder response = new StringBuilder("editConfig response = {");
+
+ Element configElement = persistedConfig;
+ NetconfMessage message = createEditConfigMessage(configElement, "/netconfOp/editConfig.xml");
+ NetconfMessage responseMessage = netconfClient.sendMessage(message);
+
+ XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument());
+ Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
+ element = element.getOnlyChildElement();
+
+ checkIsOk(element, responseMessage);
+ response.append(XmlUtil.toString(responseMessage.getDocument()));
+ response.append("}");
+ responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml"));
+
+ element = XmlElement.fromDomDocument(responseMessage.getDocument());
+ Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
+ element = element.getOnlyChildElement();
+
+ checkIsOk(element, responseMessage);
+ response.append("commit response = {");
+ response.append(XmlUtil.toString(responseMessage.getDocument()));
+ response.append("}");
+ logger.debug("Last configuration loaded successfully");
+ }
+
+ private void checkIsOk(XmlElement element, NetconfMessage responseMessage) {
+ if (element.getName().equals(XmlNetconfConstants.OK)) {
+ return;
+ } else {
+ if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) {
+ logger.warn("Can not load last configuration, operation failed");
+ throw new IllegalStateException("Can not load last configuration, operation failed: "
+ + XmlUtil.toString(responseMessage.getDocument()));
+ }
+ logger.warn("Can not load last configuration. Operation failed.");
+ throw new IllegalStateException("Can not load last configuration. Operation failed: "
+ + XmlUtil.toString(responseMessage.getDocument()));
+ }
+ }
+
+ private NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) {
+ try (InputStream stream = getClass().getResourceAsStream(editConfigResourcename)) {
+ Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcename);
+
+ Document doc = XmlUtil.readXmlToDocument(stream);
+
+ doc.getDocumentElement();
+ XmlElement editConfigElement = XmlElement.fromDomDocument(doc).getOnlyChildElement();
+ XmlElement configWrapper = editConfigElement.getOnlyChildElement(XmlNetconfConstants.CONFIG_KEY);
+ editConfigElement.getDomElement().removeChild(configWrapper.getDomElement());
+ for (XmlElement el : XmlElement.fromDomElement(dataElement).getChildElements()) {
+ configWrapper.appendChild((Element) doc.importNode(el.getDomElement(), true));
+ }
+ editConfigElement.appendChild(configWrapper.getDomElement());
+ return new NetconfMessage(doc);
+ } catch (IOException | SAXException e) {
+ throw new RuntimeException("Unable to parse message from resources " + editConfigResourcename, e);
+ }
+ }
+
+ private NetconfMessage getNetconfMessageFromResource(String resource) {
+ try (InputStream stream = getClass().getResourceAsStream(resource)) {
+ Preconditions.checkNotNull(stream, "Unable to load resource " + resource);
+ return new NetconfMessage(XmlUtil.readXmlToDocument(stream));
+ } catch (SAXException | IOException e) {
+ throw new RuntimeException("Unable to parse message from resources " + resource, e);
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ // TODO persister is received from constructor, should not be closed
+ // here
+ try {
+ persister.close();
+ } catch (Exception e) {
+ logger.warn("Unable to close config persister {}", persister, e);
+ }
+
+ if (netconfClient != null) {
+ try {
+ netconfClient.close();
+ } catch (Exception e) {
+ logger.warn("Unable to close connection to netconf {}", netconfClient, e);
+ }
+ }
+
+ // unregister from JMX
+ try {
+ if (mbeanServer.isRegistered(on)) {
+ mbeanServer.removeNotificationListener(on, this);
+ }
+ } catch (Exception e) {
+ logger.warn("Unable to unregister {} as listener for {}", this, on, e);
+ }
+ }
+}
--- /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.opendaylight.controller.config.persist.api.storage.StorageAdapter;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class NoOpStorageAdapter implements StorageAdapter {
+ private static final Logger logger = LoggerFactory.getLogger(NoOpStorageAdapter.class);
+
+ @Override
+ public void setProperties(ConfigProvider configProvider) {
+ logger.debug("setProperties called with {}", configProvider);
+ }
+
+ @Override
+ public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
+ logger.debug("persistConfig called with {}", holder);
+ }
+
+ @Override
+ public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ logger.debug("loadLastConfig called");
+ return Optional.absent();
+ }
+
+ @Override
+ public void close() throws IOException {
+ 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.Persister;
+import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+
+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(ConfigProvider configProvider) {
+ String storageAdapterClass = configProvider.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(configProvider);
+
+ } 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
+/*
+ * 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 java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+
+public final class Util {
+
+ public static ScheduledExecutorService getExecutorServiceWithThreadName(final String threadNamePrefix,
+ int threadCount) {
+ return Executors.newScheduledThreadPool(threadCount, new ThreadFactory() {
+
+ private int i = 1;
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r);
+ thread.setName(threadNamePrefix + ":" + i++);
+ return thread;
+ }
+ });
+ }
+}
--- /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.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.config.stat.ConfigProvider;
+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 org.slf4j.LoggerFactory;
+
+import javax.management.MBeanServer;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+
+public class ConfigPersisterActivator implements BundleActivator {
+
+ private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class);
+
+ private final static MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
+
+ private ConfigPersisterNotificationHandler configPersisterNotificationHandler;
+
+ private Thread initializationThread;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ logger.debug("ConfigPersister activator started");
+
+ ConfigProvider configProvider = new ConfigProvider.ConfigProviderImpl(context);
+ Optional<PersisterImpl> maybePersister = PersisterImpl.createFromProperties(configProvider);
+ if (maybePersister.isPresent() == false) {
+ throw new IllegalStateException("No persister is defined in " + PersisterImpl.STORAGE_ADAPTER_CLASS_PROP
+ + " property. For noop persister use " + NoOpStorageAdapter.class.getCanonicalName()
+ + " . Persister is not operational");
+ }
+
+ Optional<TLSConfiguration> maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(configProvider);
+ Optional<InetSocketAddress> maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(configProvider);
+
+ InetSocketAddress address;
+ if (maybeTLSConfiguration.isPresent()) {
+ throw new UnsupportedOperationException("TLS is currently not supported for persister");
+ } else if (maybeTCPAddress.isPresent()) {
+ address = maybeTCPAddress.get();
+ } else {
+ throw new IllegalStateException("Netconf is not configured, persister is not operational");
+ }
+
+ PersisterImpl persister = maybePersister.get();
+ configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(persister, address,
+ platformMBeanServer);
+ Runnable initializationRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ configPersisterNotificationHandler.init();
+ } catch (InterruptedException e) {
+ logger.info("Interrupted while waiting for netconf connection");
+ }
+ }
+ };
+ initializationThread = new Thread(initializationRunnable, "ConfigPersister-registrator");
+ initializationThread.start();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ initializationThread.interrupt();
+ configPersisterNotificationHandler.close();
+ }
+}
--- /dev/null
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <capabilities>
+ <capability>urn:ietf:params:netconf:base:1.0</capability>
+ </capabilities>
+</hello>
--- /dev/null
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="persister_commit" notify="false">
+ <commit></commit>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="persister_edit">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>replace</default-operation>
+
+ <config>
+ </config>
+
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /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.opendaylight.controller.config.stat.ConfigProvider;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.containsString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
+
+public class PersisterImplTest {
+ @Mock
+ ConfigProvider mockedConfigProvider;
+
+ @Before
+ public void setUpMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testFromProperties() throws Exception {
+ doReturn(MockAdapter.class.getName()).when(mockedConfigProvider).getProperty(
+ PersisterImpl.STORAGE_ADAPTER_CLASS_PROP);
+
+ PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedConfigProvider).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 {
+ mockedConfigProvider = mock(ConfigProvider.class);
+ doReturn(FileStorageAdapter.class.getName()).when(mockedConfigProvider).getProperty(
+ PersisterImpl.STORAGE_ADAPTER_CLASS_PROP);
+ doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when(
+ mockedConfigProvider).getProperty(FileStorageAdapter.FILE_STORAGE_PROP);
+ doReturn("mockedConfigProvider").when(mockedConfigProvider).toString();
+ doReturn(null).when(mockedConfigProvider).getProperty("numberOfBackups");
+
+ PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedConfigProvider).get();
+ assertTrue(persisterImpl.getStorage() instanceof FileStorageAdapter);
+ }
+
+ @Test
+ public void testFromProperties3() throws Exception {
+ mockedConfigProvider = mock(ConfigProvider.class);
+ doReturn(FileStorageAdapter.class.getName()).when(mockedConfigProvider).getProperty(
+ PersisterImpl.STORAGE_ADAPTER_CLASS_PROP);
+ doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when(
+ mockedConfigProvider).getProperty(FileStorageAdapter.FILE_STORAGE_PROP);
+ doReturn("mockedConfigProvider").when(mockedConfigProvider).toString();
+ doReturn("0").when(mockedConfigProvider).getProperty("numberOfBackups");
+ try {
+ PersisterImpl.createFromProperties(mockedConfigProvider).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(ConfigProvider configProvider) {
+ props++;
+ }
+
+ @Override
+ public void close() throws IOException {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+}
--- /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">
+
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>netconf-api</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ <version>0.2.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>framework</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Private-Package>
+ </Private-Package>
+ <Import-Package>
+ javax.management,
+ org.opendaylight.controller.config.api.jmx,
+ org.opendaylight.protocol.framework,
+ org.w3c.dom
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.controller.netconf.api,
+ org.opendaylight.controller.netconf.api.jmx,
+ </Export-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.api;
+
+import org.opendaylight.protocol.framework.DeserializerException;
+
+/**
+ * This exception is thrown by
+ * {@link NetconfSessionListener#onMessage(NetconfMessage)} to indicate fatal
+ * communication problem after which the session should be closed.
+ */
+public class NetconfDeserializerException extends DeserializerException {
+ private static final long serialVersionUID = 1L;
+
+ public NetconfDeserializerException(final String message) {
+ super(message);
+ }
+
+ public NetconfDeserializerException(final String message, final Exception e) {
+ super(message, e);
+ }
+}
--- /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.api;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.opendaylight.protocol.framework.DocumentedException;
+
+/**
+ * Checked exception to communicate an error that needs to be sent to the
+ * netconf client.
+ */
+public class NetconfDocumentedException extends DocumentedException {
+
+ private static final long serialVersionUID = 1L;
+
+ public enum ErrorType {
+ transport, rpc, protocol, application;
+
+ public String getTagValue() {
+ return name();
+ }
+ }
+
+ public enum ErrorTag {
+ missing_attribute("missing-attribute"), unknown_element("unknown-element"), operation_not_supported(
+ "operation-not-supported"), bad_attribute("bad-attribute"), data_missing("data-missing"), operation_failed(
+ "operation-failed"), invalid_value("invalid-value"), malformed_message("malformed-message");
+
+ private final String tagValue;
+
+ ErrorTag(final String tagValue) {
+ this.tagValue = tagValue;
+ }
+
+ public String getTagValue() {
+ return this.tagValue;
+ }
+ }
+
+ public enum ErrorSeverity {
+ error, warning;
+
+ public String getTagValue() {
+ return name();
+ }
+ }
+
+ private final ErrorType errorType;
+ private final ErrorTag errorTag;
+ private final ErrorSeverity errorSeverity;
+ private final Map<String, String> errorInfo;
+
+ public NetconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
+ final ErrorSeverity errorSeverity) {
+ this(message, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
+ }
+
+ public NetconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
+ final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
+ super(message);
+ this.errorType = errorType;
+ this.errorTag = errorTag;
+ this.errorSeverity = errorSeverity;
+ this.errorInfo = errorInfo;
+ }
+
+ public NetconfDocumentedException(final String message, final Exception cause, final ErrorType errorType,
+ final ErrorTag errorTag, final ErrorSeverity errorSeverity) {
+ this(message, cause, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
+ }
+
+ public NetconfDocumentedException(final String message, final Exception cause, final ErrorType errorType,
+ final ErrorTag errorTag, final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
+ super(message, cause);
+ this.errorType = errorType;
+ this.errorTag = errorTag;
+ this.errorSeverity = errorSeverity;
+ this.errorInfo = errorInfo;
+ }
+
+ public ErrorType getErrorType() {
+ return this.errorType;
+ }
+
+ public ErrorTag getErrorTag() {
+ return this.errorTag;
+ }
+
+ public ErrorSeverity getErrorSeverity() {
+ return this.errorSeverity;
+ }
+
+ public Map<String, String> getErrorInfo() {
+ return this.errorInfo;
+ }
+
+ @Override
+ public String toString() {
+ return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
+ + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo="
+ + this.errorInfo + '}';
+ }
+}
--- /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.api;
+
+import org.opendaylight.protocol.framework.ProtocolMessage;
+import org.w3c.dom.Document;
+
+/**
+ * NetconfMessage represents a wrapper around org.w3c.dom.Document. Needed for
+ * implementing ProtocolMessage interface.
+ */
+public final class NetconfMessage implements ProtocolMessage {
+
+ private static final long serialVersionUID = 462175939836367285L;
+
+ private final Document doc;
+
+ public NetconfMessage(final Document doc) {
+ this.doc = doc;
+ }
+
+ public Document getDocument() {
+ return this.doc;
+ }
+}
--- /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.api;
+
+import org.w3c.dom.Document;
+
+public interface NetconfOperationRouter extends AutoCloseable {
+
+ Document onNetconfMessage(Document message) throws NetconfDocumentedException;
+
+ @Override
+ 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.api;
+
+/**
+ * The only input for the start of a NETCONF session is hello-message.
+ */
+public final class NetconfServerSessionPreferences extends NetconfSessionPreferences {
+
+ private final long sessionId;
+
+ public NetconfServerSessionPreferences(final NetconfMessage helloMessage, long sessionId) {
+ super(helloMessage);
+ this.sessionId = sessionId;
+ }
+
+ public long getSessionId() {
+ return sessionId;
+ }
+}
--- /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.api;
+
+import org.opendaylight.protocol.framework.AbstractProtocolSession;
+
+public abstract class NetconfSession extends AbstractProtocolSession<NetconfMessage> {
+
+ public abstract void sendMessage(NetconfMessage netconfMessage);
+}
--- /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.api;
+
+import org.opendaylight.protocol.framework.SessionListener;
+
+public interface NetconfSessionListener extends
+ SessionListener<NetconfMessage, NetconfSession, NetconfTerminationReason> {
+
+}
--- /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.api;
+
+public class NetconfSessionPreferences {
+ protected final NetconfMessage helloMessage;
+
+ public NetconfSessionPreferences(final NetconfMessage helloMessage) {
+ this.helloMessage = helloMessage;
+ }
+
+ /**
+ * @return the helloMessage
+ */
+ public NetconfMessage getHelloMessage() {
+ return this.helloMessage;
+ }
+}
--- /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.api;
+
+import org.opendaylight.protocol.framework.TerminationReason;
+
+public class NetconfTerminationReason implements TerminationReason {
+
+ private final String reason;
+
+ public NetconfTerminationReason(String reason) {
+ this.reason = reason;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return reason;
+ }
+}
--- /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.api.jmx;
+
+import org.w3c.dom.Element;
+
+import javax.management.NotificationBroadcasterSupport;
+import java.util.Set;
+
+public class CommitJMXNotification extends NetconfJMXNotification {
+
+ private final Element configSnapshot;
+
+ private static final String afterCommitMessageTemplate = "Commit successful: %s";
+ private final Set<String> capabilities;
+
+ CommitJMXNotification(NotificationBroadcasterSupport source, String message, Element cfgSnapshot,
+ Set<String> capabilities) {
+ super(TransactionProviderJMXNotificationType.commit, source, String.format(afterCommitMessageTemplate, message));
+ this.configSnapshot = cfgSnapshot;
+ this.capabilities = capabilities;
+ }
+
+ public Element getConfigSnapshot() {
+ return configSnapshot;
+ }
+
+ public Set<String> getCapabilities() {
+ return capabilities;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("CommitJMXNotification{");
+ sb.append("configSnapshot=").append(configSnapshot);
+ sb.append(", capabilities=").append(getCapabilities());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -8587623362011695514L;
+
+}
--- /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.api.jmx;
+
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+
+import javax.management.ObjectName;
+
+public interface DefaultCommitOperationMXBean {
+
+ static String typeName = "NetconfNotificationProvider";
+ public static ObjectName objectName = ObjectNameUtil.createONWithDomainAndType(typeName);
+
+}
--- /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.api.jmx;
+
+import java.util.Set;
+
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+
+import org.w3c.dom.Element;
+
+public abstract class NetconfJMXNotification extends Notification {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6754474563863772845L;
+
+ private static long sequenceNumber = 1;
+
+ private final TransactionProviderJMXNotificationType type;
+
+ protected NetconfJMXNotification(TransactionProviderJMXNotificationType type,
+ NotificationBroadcasterSupport source, String message) {
+ super(type.toString(), source, sequenceNumber++, System.nanoTime(), message);
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return "TransactionProviderJMXNotification [type=" + type + "]";
+ }
+
+ /**
+ * Sends this notification using source that created it
+ */
+ public void send() {
+ ((NotificationBroadcasterSupport) getSource()).sendNotification(this);
+ }
+
+ /**
+ * Creates notification about successful commit execution.
+ *
+ * Intended for config-persister.
+ *
+ * @param transactionName
+ * @param cfgSnapshot
+ */
+ public static CommitJMXNotification afterCommit(NotificationBroadcasterSupport source, String message,
+ Element cfgSnapshot, Set<String> capabilities) {
+ return new CommitJMXNotification(source, message, cfgSnapshot, capabilities);
+ }
+
+ static enum TransactionProviderJMXNotificationType {
+ commit;
+ }
+
+}
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>netconf-client</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>framework</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ org.opendaylight.controller.netconf.client,
+ </Export-Package>
+ <Import-Package>
+ com.google.common.base,
+ com.google.common.collect,
+ io.netty.channel,
+ io.netty.channel.socket,
+ io.netty.util,
+ io.netty.util.concurrent,
+ javax.annotation,
+ javax.net.ssl,
+ javax.xml.namespace,
+ javax.xml.parsers,
+ javax.xml.xpath,
+ org.opendaylight.controller.netconf.api,
+ org.opendaylight.controller.netconf.util,
+ org.opendaylight.controller.netconf.util.xml,
+ org.opendaylight.protocol.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.client;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.protocol.framework.NeverReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.TimedReconnectStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+
+public class NetconfClient implements Closeable {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfClient.class);
+
+ public static final int DEFAULT_CONNECT_TIMEOUT = 5000;
+ private final NetconfClientDispatcher dispatch;
+ private final String label;
+ private final NetconfClientSession clientSession;
+ private final NetconfClientSessionListener sessionListener;
+ private final long sessionId;
+ private final InetSocketAddress address;
+
+ // TODO test reconnecting constructor
+ public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectionAttempts,
+ int attemptMsTimeout) {
+ this(clientLabelForLogging, address, getReconnectStrategy(connectionAttempts, attemptMsTimeout), Optional
+ .<SSLContext> absent());
+ }
+
+ private NetconfClient(String clientLabelForLogging, InetSocketAddress address, ReconnectStrategy strat,
+ Optional<SSLContext> maybeSSLContext) {
+ this.label = clientLabelForLogging;
+ dispatch = new NetconfClientDispatcher(maybeSSLContext);
+
+ sessionListener = new NetconfClientSessionListener();
+ Future<NetconfClientSession> clientFuture = dispatch.createClient(address, sessionListener, strat);
+ this.address = address;
+ clientSession = get(clientFuture);
+ this.sessionId = clientSession.getSessionId();
+ }
+
+ private NetconfClientSession get(Future<NetconfClientSession> clientFuture) {
+ try {
+ return clientFuture.get();
+ } catch (InterruptedException | CancellationException e) {
+ throw new RuntimeException("Netconf client interrupted", e);
+ } catch (ExecutionException e) {
+ throw new IllegalStateException("Unable to create netconf client", e);
+ }
+ }
+
+ public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectTimeoutMs,
+ Optional<SSLContext> maybeSSLContext) {
+ this(clientLabelForLogging, address,
+ new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, connectTimeoutMs), maybeSSLContext);
+ }
+
+ public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectTimeoutMs) {
+ this(clientLabelForLogging, address,
+ new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, connectTimeoutMs), Optional
+ .<SSLContext> absent());
+ }
+
+ public NetconfClient(String clientLabelForLogging, InetSocketAddress address) {
+ this(clientLabelForLogging, address, new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
+ DEFAULT_CONNECT_TIMEOUT), Optional.<SSLContext> absent());
+ }
+
+ public NetconfClient(String clientLabelForLogging, InetSocketAddress address, Optional<SSLContext> maybeSSLContext) {
+ this(clientLabelForLogging, address, new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
+ DEFAULT_CONNECT_TIMEOUT), maybeSSLContext);
+ }
+
+ public NetconfMessage sendMessage(NetconfMessage message) {
+ return sendMessage(message, 5, 1000);
+ }
+
+ public NetconfMessage sendMessage(NetconfMessage message, int attempts, int attemptMsDelay) {
+ Preconditions.checkState(clientSession.isUp(), "Session was not up yet");
+ clientSession.sendMessage(message);
+ try {
+ return sessionListener.getLastMessage(attempts, attemptMsDelay);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(this + " Cannot read message from " + address, e);
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException(this + " Cannot read message from " + address, e);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ clientSession.close();
+ dispatch.close();
+ }
+
+ private static ReconnectStrategy getReconnectStrategy(int connectionAttempts, int attemptMsTimeout) {
+ return new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, attemptMsTimeout, 1000, 1.0, null,
+ Long.valueOf(connectionAttempts), null);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("NetconfClient{");
+ sb.append("label=").append(label);
+ sb.append(", sessionId=").append(sessionId);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public long getSessionId() {
+ return sessionId;
+ }
+
+ public Set<String> getCapabilities() {
+ Preconditions.checkState(clientSession != null, "Client was not initialized successfully");
+ return Sets.newHashSet(clientSession.getServerCapabilities());
+ }
+}
--- /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.client;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.Promise;
+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.protocol.framework.AbstractDispatcher;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import java.net.InetSocketAddress;
+
+public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSession, NetconfClientSessionListener> {
+
+ private final Optional<SSLContext> maybeContext;
+ private final NetconfClientSessionNegotiatorFactory negotatorFactory;
+
+ public NetconfClientDispatcher(final Optional<SSLContext> maybeContext) {
+ this.maybeContext = Preconditions.checkNotNull(maybeContext);
+ this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(new HashedWheelTimer());
+ }
+
+ public Future<NetconfClientSession> createClient(InetSocketAddress address,
+ final NetconfClientSessionListener sessionListener, ReconnectStrategy strat) {
+
+ return super.createClient(address, strat, new PipelineInitializer<NetconfClientSession>() {
+
+ @Override
+ public void initializeChannel(final SocketChannel ch, final Promise<NetconfClientSession> promise) {
+ initialize(ch, promise);
+ }
+
+ private void initialize(SocketChannel ch, Promise<NetconfClientSession> promise) {
+ new ClientChannelInitializer(maybeContext, negotatorFactory, sessionListener).initialize(ch, promise);
+ }
+ });
+ }
+
+ private static class ClientChannelInitializer extends AbstractChannelInitializer {
+
+ private final NetconfClientSessionNegotiatorFactory negotiatorFactory;
+ private final NetconfClientSessionListener sessionListener;
+
+ private ClientChannelInitializer(Optional<SSLContext> maybeContext,
+ NetconfClientSessionNegotiatorFactory negotiatorFactory, NetconfClientSessionListener sessionListener) {
+ super(maybeContext);
+ this.negotiatorFactory = negotiatorFactory;
+ this.sessionListener = sessionListener;
+ }
+
+ @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));
+ }
+
+ @Override
+ protected void initSslEngine(SSLEngine sslEngine) {
+ sslEngine.setUseClientMode(true);
+ }
+ }
+
+}
--- /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.client;
+
+import io.netty.channel.Channel;
+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.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Collection;
+
+public class NetconfClientSession extends NetconfSession {
+
+ private final SessionListener sessionListener;
+ private final long sessionId;
+ private final Channel channel;
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfClientSession.class);
+ private final Collection<String> capabilities;
+ private boolean up;
+
+ public NetconfClientSession(SessionListener sessionListener, Channel channel, long sessionId,
+ Collection<String> capabilities) {
+ this.sessionListener = sessionListener;
+ this.channel = channel;
+ this.sessionId = sessionId;
+ this.capabilities = capabilities;
+ logger.debug("Client Session {} created", toString());
+ }
+
+ @Override
+ public void close() {
+ channel.close();
+ sessionListener.onSessionTerminated(this, new NetconfTerminationReason("Client Session closed"));
+ }
+
+ @Override
+ protected void handleMessage(NetconfMessage netconfMessage) {
+ logger.debug("Client Session {} received message {}", toString(),
+ XmlUtil.toString(netconfMessage.getDocument()));
+ sessionListener.onMessage(this, netconfMessage);
+ }
+
+ @Override
+ public void sendMessage(NetconfMessage netconfMessage) {
+ channel.writeAndFlush(netconfMessage);
+ }
+
+ @Override
+ protected void endOfInput() {
+ logger.debug("Client Session {} end of input detected while session was in state {}", toString(), isUp() ? "up"
+ : "initialized");
+ if (isUp()) {
+ this.sessionListener.onSessionDown(this, new IOException("End of input detected. Close the session."));
+ }
+ }
+
+ @Override
+ protected void sessionUp() {
+ logger.debug("Client Session {} up", toString());
+ sessionListener.onSessionUp(this);
+ this.up = true;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("ClientNetconfSession{");
+ sb.append("sessionId=").append(sessionId);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public boolean isUp() {
+ return up;
+ }
+
+ public long getSessionId() {
+ return sessionId;
+ }
+
+ public Collection<String> getServerCapabilities() {
+ return capabilities;
+ }
+}
--- /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.client;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class NetconfClientSessionListener implements
+ SessionListener<NetconfMessage, NetconfClientSession, NetconfTerminationReason> {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfClientSessionListener.class);
+ private AtomicBoolean up = new AtomicBoolean(false);
+
+ @Override
+ public void onSessionUp(NetconfClientSession clientSession) {
+ up.set(true);
+ }
+
+ @Override
+ public void onSessionDown(NetconfClientSession clientSession, Exception e) {
+ logger.debug("Client Session {} down, reason: {}", clientSession, e.getMessage());
+ up.set(false);
+ }
+
+ @Override
+ public void onSessionTerminated(NetconfClientSession clientSession,
+ NetconfTerminationReason netconfTerminationReason) {
+ logger.debug("Client Session {} terminated, reason: {}", clientSession,
+ netconfTerminationReason.getErrorMessage());
+ up.set(false);
+ }
+
+ @Override
+ public synchronized void onMessage(NetconfClientSession session, NetconfMessage message) {
+ synchronized (messages) {
+ this.messages.add(message);
+ }
+ }
+
+ private int lastReadMessage = -1;
+ private List<NetconfMessage> messages = Lists.newArrayList();
+
+ public NetconfMessage getLastMessage(int attempts, int attemptMsDelay) throws InterruptedException {
+ Preconditions.checkState(up.get(), "Session was not up yet");
+
+ for (int i = 0; i < attempts; i++) {
+ synchronized (messages) {
+ if (messages.size() - 1 > lastReadMessage) {
+ lastReadMessage++;
+ return messages.get(lastReadMessage);
+ }
+ }
+
+ if (up.get() == false)
+ throw new IllegalStateException("Session ended while trying to read message");
+ Thread.sleep(attemptMsDelay);
+ }
+
+ throw new IllegalStateException("No netconf message to read");
+ }
+}
--- /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.client;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
+import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
+import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import javax.annotation.Nullable;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import java.util.Collection;
+import java.util.List;
+
+public class NetconfClientSessionNegotiator extends
+ AbstractNetconfSessionNegotiator<NetconfSessionPreferences, NetconfClientSession> {
+
+ protected NetconfClientSessionNegotiator(NetconfSessionPreferences sessionPreferences,
+ Promise<NetconfClientSession> promise, Channel channel, Timer timer, SessionListener sessionListener) {
+ super(sessionPreferences, promise, channel, timer, sessionListener);
+ }
+
+ private static Collection<String> getCapabilities(Document doc) {
+ XmlElement responseElement = XmlElement.fromDomDocument(doc);
+ XmlElement capabilitiesElement = responseElement
+ .getOnlyChildElementWithSameNamespace(XmlNetconfConstants.CAPABILITIES);
+ List<XmlElement> caps = capabilitiesElement.getChildElements(XmlNetconfConstants.CAPABILITY);
+ return Collections2.transform(caps, new Function<XmlElement, String>() {
+
+ @Nullable
+ @Override
+ public String apply(@Nullable XmlElement input) {
+ return input.getTextContent();
+ }
+ });
+ }
+
+ private static final XPathExpression sessionIdXPath = XMLNetconfUtil
+ .compileXPath("/netconf:hello/netconf:session-id");
+
+ private long extractSessionId(Document doc) {
+ final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE);
+ String textContent = sessionIdNode.getTextContent();
+ if (textContent == null || textContent.equals("")) {
+ throw new IllegalStateException("Session id not received from server");
+ }
+
+ return Long.valueOf(textContent);
+ }
+
+ @Override
+ protected NetconfClientSession getSession(SessionListener sessionListener, Channel channel, Document doc) {
+ return new NetconfClientSession(sessionListener, channel, extractSessionId(doc), getCapabilities(doc));
+ }
+}
--- /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.client;
+
+import com.google.common.base.Preconditions;
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+import org.opendaylight.protocol.framework.SessionNegotiator;
+import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class NetconfClientSessionNegotiatorFactory implements SessionNegotiatorFactory {
+
+ private final Timer timer;
+
+ public NetconfClientSessionNegotiatorFactory(Timer timer) {
+ this.timer = timer;
+ }
+
+ private static NetconfMessage loadHelloMessageTemplate() {
+ final String helloMessagePath = "/client_hello.xml";
+ try (InputStream is = NetconfClientSessionNegotiatorFactory.class.getResourceAsStream(helloMessagePath)) {
+ Preconditions.checkState(is != null, "Input stream from %s was null", helloMessagePath);
+ return new NetconfMessage(XmlUtil.readXmlToDocument(is));
+ } catch (SAXException | IOException e) {
+ throw new RuntimeException("Unable to load hello message", e);
+ }
+ }
+
+ @Override
+ public SessionNegotiator getSessionNegotiator(SessionListenerFactory sessionListenerFactory, Channel channel,
+ Promise promise) {
+ // Hello message needs to be recreated every time
+ NetconfSessionPreferences proposal = new NetconfSessionPreferences(loadHelloMessageTemplate());
+ return new NetconfClientSessionNegotiator(proposal, promise, channel, timer,
+ sessionListenerFactory.getSessionListener());
+ }
+
+}
--- /dev/null
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <capabilities>
+ <capability>urn:ietf:params:netconf:base:1.0</capability>
+ </capabilities>
+</hello>
\ No newline at end of file
--- /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">
+
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>netconf-impl</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-util</artifactId>
+ <version>0.2.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>framework</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+
+ <artifactId>yang-store-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>xmlunit</groupId>
+ <artifactId>xmlunit</artifactId>
+ <version>1.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-client</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.netconf.impl.osgi.NetconfImplActivator</Bundle-Activator>
+ <Import-Package>
+ com.google.common.base,
+ com.google.common.collect,
+ io.netty.channel,
+ io.netty.channel.socket,
+ io.netty.util,
+ io.netty.util.concurrent,
+ javax.management,
+ javax.net.ssl,
+ javax.xml.namespace,
+ javax.xml.xpath,
+ org.opendaylight.controller.netconf.api,
+ org.opendaylight.controller.netconf.api.jmx,
+ org.opendaylight.controller.netconf.mapping.api,
+ org.opendaylight.controller.netconf.util,
+ org.opendaylight.controller.netconf.util.mapping,
+ org.opendaylight.controller.netconf.util.osgi,
+ org.opendaylight.controller.netconf.util.xml,
+ org.opendaylight.protocol.framework,
+ org.osgi.framework,
+ org.osgi.util.tracker,
+ org.slf4j,
+ org.w3c.dom,
+ org.xml.sax,
+ org.opendaylight.controller.netconf.util.messages,
+ org.opendaylight.controller.config.stat
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ </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.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+public class CapabilityProviderImpl implements CapabilityProvider {
+ private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
+ private final Set<String> capabilityURIs;
+
+ public CapabilityProviderImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
+ this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot;
+ Map<String, Capability> urisToCapabilitiesInternalMap = getCapabilitiesInternal(netconfOperationServiceSnapshot);
+ capabilityURIs = Collections.unmodifiableSet(urisToCapabilitiesInternalMap.keySet());
+ }
+
+ private static Map<String, Capability> getCapabilitiesInternal(
+ NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
+ Map<String, Capability> capabilityMap = Maps.newHashMap();
+
+ for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
+ final Set<Capability> caps = netconfOperationService.getCapabilities();
+
+ for (Capability cap : caps) {
+ // TODO check for duplicates ?
+ capabilityMap.put(cap.getCapabilityUri(), cap);
+ }
+ }
+
+ return capabilityMap;
+ }
+
+ @Override
+ public synchronized String getSchemaForCapability(String moduleName, Optional<String> revision) {
+
+ Map<String, Map<String, String>> mappedModulesToRevisionToSchema = Maps.newHashMap();
+
+ for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
+ final Set<Capability> caps = netconfOperationService.getCapabilities();
+
+ for (Capability cap : caps) {
+ if (cap.getModuleName().isPresent() == false)
+ continue;
+ if (cap.getRevision().isPresent() == false)
+ continue;
+ if (cap.getCapabilitySchema().isPresent() == false)
+ continue;
+
+ final String currentModuleName = cap.getModuleName().get();
+ Map<String, String> revisionMap = mappedModulesToRevisionToSchema.get(currentModuleName);
+ if (revisionMap == null) {
+ revisionMap = Maps.newHashMap();
+ mappedModulesToRevisionToSchema.put(currentModuleName, revisionMap);
+ }
+
+ String currentRevision = cap.getRevision().get();
+ revisionMap.put(currentRevision, cap.getCapabilitySchema().get());
+ }
+ }
+
+ Map<String, String> revisionMapRequest = mappedModulesToRevisionToSchema.get(moduleName);
+ Preconditions.checkState(revisionMapRequest != null, "Capability for module %s not present, " + ""
+ + "available modules : %s", moduleName, capabilityURIs);
+
+ if (revision.isPresent()) {
+ String schema = revisionMapRequest.get(revision.get());
+
+ Preconditions.checkState(schema != null,
+ "Capability for module %s:%s not present, available revisions for module: %s", moduleName,
+ revision.get(), revisionMapRequest.keySet());
+
+ return schema;
+ } else {
+ Preconditions.checkState(revisionMapRequest.size() == 1,
+ "Expected 1 capability for module %s, available revisions : %s", moduleName,
+ revisionMapRequest.keySet());
+ return revisionMapRequest.values().iterator().next();
+ }
+ }
+
+ @Override
+ public synchronized Set<String> getCapabilities() {
+ return capabilityURIs;
+ }
+
+}
--- /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.impl;
+
+import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
+import org.opendaylight.controller.netconf.api.jmx.DefaultCommitOperationMXBean;
+import org.opendaylight.controller.netconf.api.jmx.NetconfJMXNotification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+
+import javax.management.*;
+import java.util.Set;
+
+public class DefaultCommitNotificationProducer extends NotificationBroadcasterSupport implements
+ DefaultCommitOperationMXBean, AutoCloseable {
+
+ private static final Logger logger = LoggerFactory.getLogger(DefaultCommitNotificationProducer.class);
+
+ private final MBeanServer mbeanServer;
+
+ private final ObjectName on = DefaultCommitOperationMXBean.objectName;
+
+ public DefaultCommitNotificationProducer(MBeanServer mBeanServer) {
+ this.mbeanServer = mBeanServer;
+ registerMBean(this, mbeanServer, on);
+ }
+
+ private static void registerMBean(final Object instance, final MBeanServer mbs, final ObjectName on) {
+ try {
+ mbs.registerMBean(instance, on);
+ } catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
+ throw new RuntimeException("Unable to register " + instance + " as " + on, e);
+ }
+ }
+
+ public void sendCommitNotification(String message, Element cfgSnapshot, Set<String> capabilities) {
+ CommitJMXNotification notif = NetconfJMXNotification.afterCommit(this, message, cfgSnapshot, capabilities);
+ logger.debug("Notification about commit {} sent", notif);
+ sendNotification(notif);
+ }
+
+ @Override
+ public void close() {
+ try {
+ mbeanServer.unregisterMBean(on);
+ } catch (InstanceNotFoundException | MBeanRegistrationException e) {
+ logger.warn("Ignoring exception while unregistering {} as {}", this, on, e);
+ }
+ }
+}
--- /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.impl;
+
+import com.google.common.base.Optional;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.impl.util.DeserializerExceptionHandler;
+import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
+import org.opendaylight.protocol.framework.AbstractDispatcher;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import java.net.InetSocketAddress;
+
+public class NetconfServerDispatcher extends AbstractDispatcher<NetconfSession, NetconfServerSessionListener> {
+
+ private final ServerChannelInitializer initializer;
+
+ public NetconfServerDispatcher(final Optional<SSLContext> maybeContext,
+ NetconfServerSessionNegotiatorFactory serverNegotiatorFactory,
+ NetconfServerSessionListenerFactory listenerFactory) {
+ this.initializer = new ServerChannelInitializer(maybeContext, serverNegotiatorFactory, listenerFactory);
+ }
+
+ // FIXME change headers for all new source code files
+
+ // TODO test create server with same address twice
+ public ChannelFuture createServer(InetSocketAddress address) {
+
+ return super.createServer(address, new PipelineInitializer<NetconfSession>() {
+ @Override
+ public void initializeChannel(final SocketChannel ch, final Promise<NetconfSession> promise) {
+ initializer.initialize(ch, promise);
+ }
+ });
+ }
+
+ private static class ServerChannelInitializer extends AbstractChannelInitializer {
+
+ private final NetconfServerSessionNegotiatorFactory negotiatorFactory;
+ private final NetconfServerSessionListenerFactory listenerFactory;
+
+ private ServerChannelInitializer(Optional<SSLContext> maybeContext,
+ NetconfServerSessionNegotiatorFactory negotiatorFactory,
+ NetconfServerSessionListenerFactory listenerFactory) {
+ super(maybeContext);
+ this.negotiatorFactory = negotiatorFactory;
+ this.listenerFactory = listenerFactory;
+ }
+
+ @Override
+ protected void initializeAfterDecoder(SocketChannel ch, Promise<? extends NetconfSession> promise) {
+ ch.pipeline().addLast("deserializerExHandler", new DeserializerExceptionHandler());
+ ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(listenerFactory, ch, promise));
+ }
+
+ @Override
+ protected void initSslEngine(SSLEngine sslEngine) {
+ sslEngine.setUseClientMode(false);
+ }
+ }
+
+}
--- /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.impl;
+
+import io.netty.channel.Channel;
+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.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class NetconfServerSession extends NetconfSession {
+
+ private final SessionListener sessionListener;
+ private final Channel channel;
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfServerSession.class);
+ private final long sessionId;
+ private boolean up = false;
+
+ public NetconfServerSession(SessionListener sessionListener, Channel channel, long sessionId) {
+ this.sessionListener = sessionListener;
+ this.channel = channel;
+ this.sessionId = sessionId;
+ logger.debug("Session {} created", toString());
+ }
+
+ @Override
+ public void close() {
+ channel.close();
+ sessionListener.onSessionTerminated(this, new NetconfTerminationReason("Session closed"));
+ }
+
+ @Override
+ protected void handleMessage(NetconfMessage netconfMessage) {
+ logger.debug("Session {} received message {}", toString(), XmlUtil.toString(netconfMessage.getDocument()));
+ sessionListener.onMessage(this, netconfMessage);
+ }
+
+ public void sendMessage(NetconfMessage netconfMessage) {
+ channel.writeAndFlush(netconfMessage);
+ }
+
+ @Override
+ protected void endOfInput() {
+ logger.debug("Session {} end of input detected while session was in state {}", toString(), isUp() ? "up"
+ : "initialized");
+ if (isUp()) {
+ this.sessionListener.onSessionDown(this, new IOException("End of input detected. Close the session."));
+ }
+ }
+
+ @Override
+ protected void sessionUp() {
+ logger.debug("Session {} up", toString());
+ sessionListener.onSessionUp(this);
+ this.up = true;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("ServerNetconfSession{");
+ sb.append("sessionId=").append(sessionId);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public boolean isUp() {
+ return up;
+ }
+
+ public long getSessionId() {
+ return sessionId;
+ }
+}
--- /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.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
+import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class NetconfServerSessionListener implements
+ SessionListener<NetconfMessage, NetconfServerSession, NetconfTerminationReason> {
+
+ static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionListener.class);
+ public static final String MESSAGE_ID = "message-id";
+
+ private NetconfOperationRouterImpl operationRouter;
+
+ public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter) {
+ this.operationRouter = operationRouter;
+ }
+
+ @Override
+ public void onSessionUp(NetconfServerSession netconfNetconfServerSession) {
+
+ }
+
+ @Override
+ public void onSessionDown(NetconfServerSession netconfNetconfServerSession, Exception e) {
+ logger.debug("Session {} down, reason: {}", netconfNetconfServerSession, e.getMessage());
+
+ operationRouter.close();
+ }
+
+ @Override
+ public void onSessionTerminated(NetconfServerSession netconfNetconfServerSession,
+ NetconfTerminationReason netconfTerminationReason) {
+ logger.debug("Session {} terminated, reason: {}", netconfNetconfServerSession,
+ netconfTerminationReason.getErrorMessage());
+
+ operationRouter.close();
+ }
+
+ @Override
+ public void onMessage(NetconfServerSession session, NetconfMessage netconfMessage) {
+ try {
+
+ Preconditions.checkState(operationRouter != null, "Cannot handle message, session up was not yet received");
+ // FIXME: there is no validation since the document may contain yang
+ // schemas
+ final NetconfMessage message = processDocument(netconfMessage);
+ logger.debug("Respondign with message {}", XmlUtil.toString(message.getDocument()));
+ session.sendMessage(message);
+
+ if (isCloseSession(netconfMessage)) {
+ closeNetconfSession(session);
+ }
+
+ } catch (final RuntimeException e) {
+ logger.error("Unexpected exception", e);
+ // TODO: should send generic error or close session?
+ throw new RuntimeException("Unable to process incoming message " + netconfMessage, e);
+ } catch (NetconfDocumentedException e) {
+ SendErrorExceptionUtil.sendErrorMessage(session, e, netconfMessage);
+ }
+ }
+
+ private void closeNetconfSession(NetconfServerSession session) {
+ // destroy NetconfOperationService
+ session.close();
+ logger.info("Session {} closed successfully", session.getSessionId());
+ }
+
+ private NetconfMessage processDocument(final NetconfMessage netconfMessage) throws NetconfDocumentedException {
+
+ final Document incommingDocument = netconfMessage.getDocument();
+ final Node rootNode = incommingDocument.getDocumentElement();
+
+ if (rootNode.getNodeName().equals(XmlNetconfConstants.RPC_KEY)) {
+ final String messageId = rootNode.getAttributes().getNamedItem(MESSAGE_ID).getTextContent();
+ checkState(messageId != null);
+ final Document responseDocument = XmlUtil.newDocument();
+ Document rpcReply = operationRouter.onNetconfMessage(incommingDocument);
+ responseDocument.appendChild(responseDocument.importNode(rpcReply.getDocumentElement(), true));
+ return new NetconfMessage(responseDocument);
+ } else {
+ // unknown command, send RFC 4741 p.70 unknown-element
+ /*
+ * Tag: unknown-element Error-type: rpc, protocol, application
+ * Severity: error Error-info: <bad-element> : name of the
+ * unexpected element Description: An unexpected element is present.
+ */
+ // TODO add message to error info
+ throw new NetconfDocumentedException("Unknown tag " + rootNode.getNodeName(),
+ NetconfDocumentedException.ErrorType.protocol, NetconfDocumentedException.ErrorTag.unknown_element,
+ NetconfDocumentedException.ErrorSeverity.error, ImmutableMap.of("bad-element",
+ rootNode.getNodeName()));
+ }
+ }
+
+ private static boolean isCloseSession(final NetconfMessage incommingDocument) {
+ final Document document = incommingDocument.getDocument();
+ XmlElement rpcElement = XmlElement.fromDomDocument(document);
+ if (rpcElement.getOnlyChildElementOptionally("close-session").isPresent())
+ return true;
+
+ return false;
+ }
+}
--- /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.impl;
+
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+
+public class NetconfServerSessionListenerFactory implements SessionListenerFactory<NetconfServerSessionListener> {
+
+ private final NetconfOperationServiceFactoryListener factoriesListener;
+
+ private final DefaultCommitNotificationProducer commitNotifier;
+
+ private final SessionIdProvider idProvider;
+
+ public NetconfServerSessionListenerFactory(NetconfOperationServiceFactoryListener factoriesListener,
+ DefaultCommitNotificationProducer commitNotifier, SessionIdProvider idProvider) {
+ this.factoriesListener = factoriesListener;
+ this.commitNotifier = commitNotifier;
+ this.idProvider = idProvider;
+ }
+
+ @Override
+ public NetconfServerSessionListener getSessionListener() {
+ NetconfOperationServiceSnapshot netconfOperationServiceSnapshot = factoriesListener.getSnapshot(idProvider
+ .getCurrentSessionId());
+
+ CapabilityProvider capabilityProvider = new CapabilityProviderImpl(netconfOperationServiceSnapshot);
+
+ NetconfOperationRouterImpl operationRouter = new NetconfOperationRouterImpl(netconfOperationServiceSnapshot,
+ capabilityProvider, commitNotifier);
+
+ return new NetconfServerSessionListener(operationRouter);
+ }
+}
--- /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.impl;
+
+import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
+import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.w3c.dom.Document;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+
+public class NetconfServerSessionNegotiator extends
+ AbstractNetconfSessionNegotiator<NetconfServerSessionPreferences, NetconfServerSession> {
+
+ protected NetconfServerSessionNegotiator(NetconfServerSessionPreferences sessionPreferences,
+ Promise<NetconfServerSession> promise, Channel channel, Timer timer, SessionListener sessionListener) {
+ super(sessionPreferences, promise, channel, timer, sessionListener);
+ }
+
+ @Override
+ protected NetconfServerSession getSession(SessionListener sessionListener, Channel channel, Document doc) {
+ return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId());
+ }
+
+}
--- /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.impl;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
+import org.opendaylight.controller.netconf.impl.util.NetconfUtil;
+import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+import org.opendaylight.protocol.framework.SessionNegotiator;
+import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+
+public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorFactory {
+
+ private final Timer timer;
+
+ private static final Document helloMessageTemplate = loadHelloMessageTemplate();
+ private final SessionIdProvider idProvider;
+ private final NetconfOperationServiceFactoryListener factoriesListener;
+
+ public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationServiceFactoryListener factoriesListener,
+ SessionIdProvider idProvider) {
+ this.timer = timer;
+ this.factoriesListener = factoriesListener;
+ this.idProvider = idProvider;
+ }
+
+ private static Document loadHelloMessageTemplate() {
+ return NetconfUtil.createMessage(
+ NetconfServerSessionNegotiatorFactory.class.getResourceAsStream("/server_hello.xml")).getDocument();
+ }
+
+ @Override
+ public SessionNegotiator getSessionNegotiator(SessionListenerFactory sessionListenerFactory, Channel channel,
+ Promise promise) {
+ long sessionId = idProvider.getNextSessionId();
+
+ NetconfServerSessionPreferences proposal = new NetconfServerSessionPreferences(createHelloMessage(sessionId),
+ sessionId);
+ return new NetconfServerSessionNegotiator(proposal, promise, channel, timer,
+ sessionListenerFactory.getSessionListener());
+ }
+
+ private static final XPathExpression sessionIdXPath = XMLNetconfUtil
+ .compileXPath("/netconf:hello/netconf:session-id");
+ private static final XPathExpression capabilitiesXPath = XMLNetconfUtil
+ .compileXPath("/netconf:hello/netconf:capabilities");
+
+ private NetconfMessage createHelloMessage(long sessionId) {
+ Document helloMessageTemplate = getHelloTemplateClone();
+
+ // change session ID
+ final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, helloMessageTemplate,
+ XPathConstants.NODE);
+ sessionIdNode.setTextContent(String.valueOf(sessionId));
+
+ // add capabilities from yang store
+ final Element capabilitiesElement = (Element) XmlUtil.evaluateXPath(capabilitiesXPath, helloMessageTemplate,
+ XPathConstants.NODE);
+
+ CapabilityProvider capabilityProvider = new CapabilityProviderImpl(factoriesListener.getSnapshot(sessionId));
+
+ for (String capability : capabilityProvider.getCapabilities()) {
+ final Element capabilityElement = helloMessageTemplate.createElement(XmlNetconfConstants.CAPABILITY);
+ capabilityElement.setTextContent(capability);
+ capabilitiesElement.appendChild(capabilityElement);
+ }
+ return new NetconfMessage(helloMessageTemplate);
+ }
+
+ private synchronized Document getHelloTemplateClone() {
+ return (Document) this.helloMessageTemplate.cloneNode(true);
+ }
+}
--- /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.impl;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public final class SessionIdProvider {
+
+ private final AtomicLong sessionCounter = new AtomicLong(0);
+
+ public long getNextSessionId() {
+ return sessionCounter.incrementAndGet();
+ }
+
+ public long getCurrentSessionId() {
+ return sessionCounter.get();
+ }
+}
--- /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.impl.mapping;
+
+import com.google.common.base.Optional;
+
+import java.util.Set;
+
+public interface CapabilityProvider {
+
+ String getSchemaForCapability(String moduleName, Optional<String> revision);
+
+ 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.netconf.impl.mapping.operations;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class DefaultCloseSession extends AbstractNetconfOperation {
+ public static final String CLOSE_SESSION = "close-session";
+
+ public DefaultCloseSession(String netconfSessionIdForReporting) {
+ super(netconfSessionIdForReporting);
+ }
+
+ @Override
+ protected HandlingPriority canHandle(String operationName, String netconfOperationNamespace) {
+ if (operationName.equals(CLOSE_SESSION) == false)
+ return HandlingPriority.CANNOT_HANDLE;
+ if (netconfOperationNamespace.equals(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0) == false)
+ return HandlingPriority.CANNOT_HANDLE;
+
+ return HandlingPriority.HANDLE_WITH_MAX_PRIORITY;
+ }
+
+ /**
+ * Close netconf operation router associated to this session, which in turn
+ * closes NetconfOperationServiceSnapshot with all NetconfOperationService
+ * instances
+ */
+ @Override
+ protected Element handle(Document document, XmlElement operationElement, NetconfOperationRouter opRouter)
+ throws NetconfDocumentedException {
+ opRouter.close();
+ return document.createElement(XmlNetconfConstants.OK);
+ }
+}
--- /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.impl.mapping.operations;
+
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilterChain;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation.OperationNameAndNamespace;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.io.InputStream;
+import java.util.Map;
+
+public class DefaultCommit implements NetconfOperationFilter {
+
+ private static final Logger logger = LoggerFactory.getLogger(DefaultCommit.class);
+
+ private static final String NOTIFY_ATTR = "notify";
+
+ private final DefaultCommitNotificationProducer notificationProducer;
+ private final CapabilityProvider cap;
+ private final String netconfSessionIdForReporting;
+
+ public DefaultCommit(DefaultCommitNotificationProducer notifier, CapabilityProvider cap,
+ String netconfSessionIdForReporting) {
+ this.notificationProducer = notifier;
+ this.cap = cap;
+ this.netconfSessionIdForReporting = netconfSessionIdForReporting;
+ this.getConfigMessage = loadGetConfigMessage();
+ }
+
+ private final Document getConfigMessage;
+ public static final String GET_CONFIG_CANDIDATE_XML_LOCATION = "/getConfig_candidate.xml";
+
+ private static Document loadGetConfigMessage() {
+ try (InputStream asStream = DefaultCommit.class.getResourceAsStream(GET_CONFIG_CANDIDATE_XML_LOCATION)) {
+ return XmlUtil.readXmlToDocument(asStream);
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to load getConfig message for notifications from "
+ + GET_CONFIG_CANDIDATE_XML_LOCATION);
+ }
+ }
+
+ @Override
+ public Document doFilter(Document message, NetconfOperationRouter operationRouter,
+ NetconfOperationFilterChain filterChain) throws NetconfDocumentedException {
+ OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(message);
+ if (canHandle(operationNameAndNamespace)) {
+ if (isCommitWithoutNotification(message)) {
+ message = removePersisterAttributes(message);
+ logger.debug("Skipping commit notification");
+ // fall back to filter chain
+ } else {
+ Document innerResult = filterChain.execute(message, operationRouter);
+ Element cfgSnapshot = getConfigSnapshot(operationRouter);
+ logger.debug("Config snapshot retrieved successfully {}", cfgSnapshot);
+ notificationProducer.sendCommitNotification("ok", cfgSnapshot, cap.getCapabilities());
+ return innerResult;
+ }
+ }
+ return filterChain.execute(message, operationRouter);
+ }
+
+ @Override
+ public int getSoringOrder() {
+ return 0;
+ }
+
+ @Override
+ public int compareTo(NetconfOperationFilter o) {
+ return Integer.compare(getSoringOrder(), o.getSoringOrder());
+ }
+
+ private boolean canHandle(OperationNameAndNamespace operationNameAndNamespace) {
+ if (operationNameAndNamespace.getOperationName().equals("commit") == false)
+ return false;
+ return operationNameAndNamespace.getNamespace().equals(
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ }
+
+ private Document removePersisterAttributes(Document message) {
+ final Element documentElement = message.getDocumentElement();
+ documentElement.removeAttribute(NOTIFY_ATTR);
+ return message;
+ }
+
+ private boolean isCommitWithoutNotification(Document message) {
+ XmlElement xmlElement = XmlElement.fromDomElementWithExpected(message.getDocumentElement(),
+ XmlNetconfConstants.RPC_KEY, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+ String attr = xmlElement.getAttribute(NOTIFY_ATTR);
+
+ if (attr == null || attr.equals(""))
+ return false;
+ else if (attr.equals(Boolean.toString(false))) {
+ logger.debug("Commit operation received with notify=false attribute {}", message);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private Element getConfigSnapshot(NetconfOperationRouter opRouter) throws NetconfDocumentedException {
+ final Document responseDocument = opRouter.onNetconfMessage(getConfigMessage);
+
+ XmlElement dataElement;
+ try {
+ XmlElement xmlElement = XmlElement.fromDomElementWithExpected(responseDocument.getDocumentElement(),
+ XmlNetconfConstants.RPC_REPLY_KEY, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ dataElement = xmlElement.getOnlyChildElement(XmlNetconfConstants.DATA_KEY);
+ } catch (IllegalArgumentException e) {
+ final String msg = "Unexpected response from get-config operation";
+ logger.warn(msg, e);
+ Map<String, String> info = Maps.newHashMap();
+ info.put(NetconfDocumentedException.ErrorTag.operation_failed.toString(), e.getMessage());
+ throw new NetconfDocumentedException(msg, e, NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_failed,
+ NetconfDocumentedException.ErrorSeverity.error, info);
+ }
+
+ return dataElement.getDomElement();
+ }
+
+ @Override
+ public String toString() {
+ return "DefaultCommit{" + netconfSessionIdForReporting + '}';
+ }
+}
--- /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.impl.mapping.operations;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public final class DefaultGetSchema extends AbstractNetconfOperation {
+
+ private final CapabilityProvider cap;
+
+ private static final Logger logger = LoggerFactory.getLogger(DefaultGetSchema.class);
+
+ public DefaultGetSchema(CapabilityProvider cap, String netconfSessionIdForReporting) {
+ super(netconfSessionIdForReporting);
+ this.cap = cap;
+ }
+
+ public static final String GET_SCHEMA = "get-schema";
+ public static final String IDENTIFIER = "identifier";
+ public static final String VERSION = "version";
+
+ @Override
+ protected HandlingPriority canHandle(String netconfOperationName, String namespace) {
+ if (netconfOperationName.equals("get-schema") == false)
+ return HandlingPriority.CANNOT_HANDLE;
+ if (namespace.equals(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING) == false)
+ return HandlingPriority.CANNOT_HANDLE;
+
+ return HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY;
+ }
+
+ @Override
+ protected Element handle(Document document, XmlElement xml, NetconfOperationRouter router)
+ throws NetconfDocumentedException {
+ GetSchemaEntry entry;
+
+ try {
+ entry = new GetSchemaEntry(xml);
+ } catch (final IllegalArgumentException e) {
+ logger.warn("Error parsing xml", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(NetconfDocumentedException.ErrorTag.bad_attribute.name(), e.getMessage());
+ throw new NetconfDocumentedException(e.getMessage(), e, NetconfDocumentedException.ErrorType.rpc,
+ NetconfDocumentedException.ErrorTag.bad_attribute, NetconfDocumentedException.ErrorSeverity.error,
+ errorInfo);
+ } catch (final IllegalStateException e) {
+ logger.warn("Error parsing xml", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(NetconfDocumentedException.ErrorTag.bad_attribute.name(), e.getMessage());
+ throw new NetconfDocumentedException(e.getMessage(), e, NetconfDocumentedException.ErrorType.rpc,
+ NetconfDocumentedException.ErrorTag.bad_attribute, NetconfDocumentedException.ErrorSeverity.error,
+ errorInfo);
+ }
+
+ String schema;
+ try {
+ schema = cap.getSchemaForCapability(entry.identifier, entry.version);
+ } catch (IllegalStateException e) {
+ Map<String, String> errorInfo = Maps.newHashMap();
+ errorInfo.put(entry.identifier, e.getMessage());
+ logger.warn("Rpc error: {}", NetconfDocumentedException.ErrorTag.operation_failed, e);
+ throw new NetconfDocumentedException(e.getMessage(), NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_failed,
+ NetconfDocumentedException.ErrorSeverity.error, errorInfo);
+ }
+
+ Element getSchemaResult;
+ getSchemaResult = XmlUtil.createTextElement(document, XmlNetconfConstants.DATA_KEY, schema);
+ XmlUtil.addNamespaceAttr(getSchemaResult,
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING);
+
+ logger.info("{} operation successful", GET_SCHEMA);
+
+ return getSchemaResult;
+ }
+
+ private static final class GetSchemaEntry {
+ private final String identifier;
+ private final Optional<String> version;
+
+ GetSchemaEntry(XmlElement getSchemaElement) {
+ getSchemaElement.checkName(GET_SCHEMA);
+ getSchemaElement.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING);
+
+ XmlElement identifierElement = getSchemaElement.getOnlyChildElementWithSameNamespace(IDENTIFIER);
+ identifier = identifierElement.getTextContent();
+ Optional<XmlElement> versionElement = getSchemaElement
+ .getOnlyChildElementWithSameNamespaceOptionally(VERSION);
+ if (versionElement.isPresent()) {
+ version = Optional.of(versionElement.get().getTextContent());
+ } else {
+ version = Optional.absent();
+ }
+
+ }
+ }
+}
--- /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.impl.osgi;
+
+import com.google.common.base.Optional;
+import io.netty.util.HashedWheelTimer;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+import org.opendaylight.controller.netconf.impl.*;
+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 org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+
+public class NetconfImplActivator implements BundleActivator {
+
+ 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 {
+ final ConfigProvider configProvider = new ConfigProvider.ConfigProviderImpl(context);
+ maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(configProvider);
+ maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(configProvider);
+ if (maybeTCPAddress.isPresent() == false && maybeTLSConfiguration.isPresent() == false) {
+ throw new IllegalStateException("TCP nor TLS is configured, netconf not available.");
+ }
+ NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+ factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
+ factoriesTracker.open();
+
+ SessionIdProvider idProvider = new SessionIdProvider();
+ NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+ new HashedWheelTimer(), factoriesListener, idProvider);
+
+ commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+
+ NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+ factoriesListener, commitNot, idProvider);
+
+ if (maybeTCPAddress.isPresent()) {
+ Optional<SSLContext> maybeSSLContext = Optional.absent();
+ InetSocketAddress address = maybeTCPAddress.get();
+ dispatch = new NetconfServerDispatcher(maybeSSLContext, serverNegotiatorFactory, listenerFactory);
+
+ 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();
+ dispatch = new NetconfServerDispatcher(maybeSSLContext, serverNegotiatorFactory, listenerFactory);
+
+ logger.info("Starting TLS netconf server at {}", address);
+ dispatch.createServer(address);
+ }
+ }
+
+ @Override
+ public void stop(final BundleContext context) throws Exception {
+ logger.info("Shutting down netconf because YangStoreService service was removed");
+
+ commitNot.close();
+ dispatch.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.impl.osgi;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCommit;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultGetSchema;
+import org.opendaylight.controller.netconf.mapping.api.*;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+
+import java.util.*;
+
+public class NetconfOperationRouterImpl implements NetconfOperationRouter {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfOperationRouterImpl.class);
+
+ private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
+
+ private final Set<NetconfOperation> allNetconfOperations;
+ private final TreeSet<NetconfOperationFilter> allSortedFilters;
+
+ private final CapabilityProvider capabilityProvider;
+
+ public NetconfOperationRouterImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot,
+ CapabilityProvider capabilityProvider, DefaultCommitNotificationProducer commitNotifier) {
+
+ this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot;
+
+ this.capabilityProvider = capabilityProvider;
+
+ Set<NetconfOperation> defaultNetconfOperations = Sets.newHashSet();
+ defaultNetconfOperations.add(new DefaultGetSchema(capabilityProvider, netconfOperationServiceSnapshot
+ .getNetconfSessionIdForReporting()));
+ defaultNetconfOperations.add(new DefaultCloseSession(netconfOperationServiceSnapshot
+ .getNetconfSessionIdForReporting()));
+
+ allNetconfOperations = getAllNetconfOperations(defaultNetconfOperations, netconfOperationServiceSnapshot);
+
+ DefaultCommit defaultCommit = new DefaultCommit(commitNotifier, capabilityProvider,
+ netconfOperationServiceSnapshot.getNetconfSessionIdForReporting());
+ Set<NetconfOperationFilter> defaultFilters = Sets.<NetconfOperationFilter> newHashSet(defaultCommit);
+ allSortedFilters = getAllNetconfFilters(defaultFilters, netconfOperationServiceSnapshot);
+ }
+
+ private static Set<NetconfOperation> getAllNetconfOperations(Set<NetconfOperation> defaultNetconfOperations,
+ NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
+ Set<NetconfOperation> result = new HashSet<>();
+ result.addAll(defaultNetconfOperations);
+
+ for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
+ final Set<NetconfOperation> netOpsFromService = netconfOperationService.getNetconfOperations();
+ for (NetconfOperation netconfOperation : netOpsFromService) {
+ Preconditions.checkState(result.contains(netconfOperation) == false,
+ "Netconf operation %s already present", netconfOperation);
+ result.add(netconfOperation);
+ }
+ }
+ return Collections.unmodifiableSet(result);
+ }
+
+ private static TreeSet<NetconfOperationFilter> getAllNetconfFilters(Set<NetconfOperationFilter> defaultFilters,
+ NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
+ TreeSet<NetconfOperationFilter> result = new TreeSet<>(defaultFilters);
+ for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
+ final Set<NetconfOperationFilter> filtersFromService = netconfOperationService.getFilters();
+ for (NetconfOperationFilter filter : filtersFromService) {
+ Preconditions.checkState(result.contains(filter) == false, "Filter %s already present", filter);
+ result.add(filter);
+ }
+ }
+ return result;
+ }
+
+ public CapabilityProvider getCapabilityProvider() {
+ return capabilityProvider;
+ }
+
+ @Override
+ public synchronized Document onNetconfMessage(Document message) throws NetconfDocumentedException {
+ NetconfOperationExecution netconfOperationExecution = getNetconfOperationWithHighestPriority(message);
+ logger.debug("Forwarding netconf message {} to {}", XmlUtil.toString(message),
+ netconfOperationExecution.operationWithHighestPriority);
+
+ final LinkedList<NetconfOperationFilterChain> chain = new LinkedList<>();
+ chain.push(netconfOperationExecution);
+
+ for (Iterator<NetconfOperationFilter> it = allSortedFilters.descendingIterator(); it.hasNext();) {
+ final NetconfOperationFilter filter = it.next();
+ final NetconfOperationFilterChain prevItem = chain.getFirst();
+ NetconfOperationFilterChain currentItem = new NetconfOperationFilterChain() {
+ @Override
+ public Document execute(Document message, NetconfOperationRouter operationRouter)
+ throws NetconfDocumentedException {
+ logger.trace("Entering {}", filter);
+ return filter.doFilter(message, operationRouter, prevItem);
+ }
+ };
+ chain.push(currentItem);
+ }
+ return chain.getFirst().execute(message, this);
+ }
+
+ private NetconfOperationExecution getNetconfOperationWithHighestPriority(Document message) {
+
+ // TODO test
+ TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = getSortedNetconfOperationsWithCanHandle(message);
+
+ Preconditions.checkState(sortedPriority.isEmpty() == false, "No %s available to handle message %s",
+ NetconfOperation.class.getName(), XmlUtil.toString(message));
+
+ HandlingPriority highestFoundPriority = sortedPriority.lastKey();
+
+ int netconfOperationsWithHighestPriority = sortedPriority.get(highestFoundPriority).size();
+
+ Preconditions.checkState(netconfOperationsWithHighestPriority == 1,
+ "Multiple %s available to handle message %s", NetconfOperation.class.getName(), message);
+
+ return new NetconfOperationExecution(sortedPriority, highestFoundPriority);
+ }
+
+ private TreeMap<HandlingPriority, Set<NetconfOperation>> getSortedNetconfOperationsWithCanHandle(Document message) {
+ TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = Maps.newTreeMap();
+
+ for (NetconfOperation netconfOperation : allNetconfOperations) {
+ final HandlingPriority handlingPriority = netconfOperation.canHandle(message);
+
+ if (handlingPriority.equals(HandlingPriority.CANNOT_HANDLE) == false) {
+ Set<NetconfOperation> netconfOperations = sortedPriority.get(handlingPriority);
+ netconfOperations = checkIfNoOperationsOnPriority(sortedPriority, handlingPriority, netconfOperations);
+ netconfOperations.add(netconfOperation);
+ }
+ }
+ return sortedPriority;
+ }
+
+ private Set<NetconfOperation> checkIfNoOperationsOnPriority(
+ TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority, HandlingPriority handlingPriority,
+ Set<NetconfOperation> netconfOperations) {
+ if (netconfOperations == null) {
+ netconfOperations = Sets.newHashSet();
+ sortedPriority.put(handlingPriority, netconfOperations);
+ }
+ return netconfOperations;
+ }
+
+ @Override
+ public void close() {
+ netconfOperationServiceSnapshot.close();
+ }
+
+ private class NetconfOperationExecution implements NetconfOperationFilterChain {
+ private final NetconfOperation operationWithHighestPriority;
+
+ private NetconfOperationExecution(NetconfOperation operationWithHighestPriority) {
+ this.operationWithHighestPriority = operationWithHighestPriority;
+ }
+
+ public NetconfOperationExecution(TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority,
+ HandlingPriority highestFoundPriority) {
+ operationWithHighestPriority = sortedPriority.get(highestFoundPriority).iterator().next();
+ sortedPriority.remove(highestFoundPriority);
+ }
+
+ @Override
+ public Document execute(Document message, NetconfOperationRouter router) throws NetconfDocumentedException {
+ return operationWithHighestPriority.handle(message, router);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot
+ + '}';
+ }
+}
--- /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.impl.osgi;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+
+public interface NetconfOperationServiceFactoryListener {
+
+ void onAddNetconfOperationServiceFactory(NetconfOperationServiceFactory service);
+
+ void onRemoveNetconfOperationServiceFactory(NetconfOperationServiceFactory service);
+
+ NetconfOperationServiceSnapshot getSnapshot(long sessionId);
+}
--- /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.impl.osgi;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+
+public class NetconfOperationServiceFactoryListenerImpl implements NetconfOperationServiceFactoryListener {
+ private final Set<NetconfOperationServiceFactory> allFactories = new HashSet<>();
+
+ @Override
+ public synchronized void onAddNetconfOperationServiceFactory(NetconfOperationServiceFactory service) {
+ allFactories.add(service);
+ }
+
+ @Override
+ public synchronized void onRemoveNetconfOperationServiceFactory(NetconfOperationServiceFactory service) {
+ allFactories.remove(service);
+ }
+
+ @Override
+ public synchronized NetconfOperationServiceSnapshot getSnapshot(long sessionId) {
+ return new NetconfOperationServiceSnapshot(allFactories, sessionId);
+ }
+
+}
--- /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.impl.osgi;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+class NetconfOperationServiceFactoryTracker extends
+ ServiceTracker<NetconfOperationServiceFactory, NetconfOperationServiceFactory> {
+ private final NetconfOperationServiceFactoryListener operationRouter;
+
+ NetconfOperationServiceFactoryTracker(BundleContext context,
+ final NetconfOperationServiceFactoryListener operationRouter) {
+ super(context, NetconfOperationServiceFactory.class, null);
+ this.operationRouter = operationRouter;
+ }
+
+ @Override
+ public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
+ NetconfOperationServiceFactory netconfOperationServiceFactory = super.addingService(reference);
+ operationRouter.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ return netconfOperationServiceFactory;
+ }
+
+ @Override
+ public void removedService(ServiceReference<NetconfOperationServiceFactory> reference,
+ NetconfOperationServiceFactory netconfOperationServiceFactory) {
+ operationRouter.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ }
+
+}
--- /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.impl.osgi;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfOperationServiceSnapshot implements AutoCloseable {
+ private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceSnapshot.class);
+
+ private final Set<NetconfOperationService> services;
+ private final String netconfSessionIdForReporting;
+
+ public NetconfOperationServiceSnapshot(Set<NetconfOperationServiceFactory> factories, long sessionId) {
+ Set<NetconfOperationService> services = new HashSet<>();
+ netconfSessionIdForReporting = getNetconfSessionIdForReporting(sessionId);
+ for (NetconfOperationServiceFactory factory : factories) {
+ services.add(factory.createService(sessionId, netconfSessionIdForReporting));
+ }
+ this.services = Collections.unmodifiableSet(services);
+ }
+
+ private static String getNetconfSessionIdForReporting(long sessionId) {
+ return "netconf session id " + sessionId;
+ }
+
+ public String getNetconfSessionIdForReporting() {
+ return netconfSessionIdForReporting;
+ }
+
+ public Set<NetconfOperationService> getServices() {
+ return services;
+ }
+
+ @Override
+ public void close() {
+ RuntimeException firstException = null;
+ for (NetconfOperationService service : services) {
+ try {
+ service.close();
+ } catch (RuntimeException e) {
+ logger.warn("Got exception while closing {}", service, e);
+ if (firstException == null) {
+ firstException = e;
+ } else {
+ firstException.addSuppressed(e);
+ }
+ }
+ }
+ if (firstException != null) {
+ throw firstException;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "NetconfOperationServiceSnapshot{" + netconfSessionIdForReporting + '}';
+ }
+}
--- /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.impl.util;
+
+import java.util.Map;
+
+import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
+
+import com.google.common.collect.Maps;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+
+public final class DeserializerExceptionHandler implements ChannelHandler {
+
+ @Override
+ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+ // NOOP
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ // NOOP
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ if (cause instanceof NetconfDeserializerException) {
+ handleDeserializerException(ctx, cause);
+ }
+ }
+
+ private void handleDeserializerException(ChannelHandlerContext ctx, Throwable cause) {
+
+ Map<String, String> info = Maps.newHashMap();
+ info.put("cause", cause.getMessage());
+ NetconfDocumentedException ex = new NetconfDocumentedException(cause.getMessage(),
+ NetconfDocumentedException.ErrorType.rpc, NetconfDocumentedException.ErrorTag.malformed_message,
+ NetconfDocumentedException.ErrorSeverity.error, info);
+
+ SendErrorExceptionUtil.sendErrorMessage(ctx.channel(), ex);
+ }
+}
--- /dev/null
+package org.opendaylight.controller.netconf.impl.util;
+
+import java.io.*;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+// TODO purge nulls
+public class NetconfUtil {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfUtil.class);
+
+ public static NetconfMessage createMessage(final File f) {
+ try {
+ return createMessage(new FileInputStream(f));
+ } catch (final FileNotFoundException e) {
+ logger.warn("File {} not found.", f, e);
+ }
+ return null;
+ }
+
+ public static NetconfMessage createMessage(final InputStream is) {
+ Document doc = null;
+ try {
+ doc = XmlUtil.readXmlToDocument(is);
+ } catch (final IOException e) {
+ logger.warn("Error ocurred while parsing stream.", e);
+ } catch (final SAXException e) {
+ logger.warn("Error ocurred while final parsing stream.", e);
+ }
+ return (doc == null) ? null : new NetconfMessage(doc);
+ }
+}
--- /dev/null
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="commitNotification">
+ <get-config>
+ <source>
+ <candidate/>
+ </source>
+ </get-config>
+</rpc>
--- /dev/null
+<?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.0</capability>
+ </capabilities>
+ <session-id>1</session-id>
+</hello>
--- /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.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import io.netty.channel.ChannelFuture;
+import io.netty.util.HashedWheelTimer;
+import org.apache.commons.io.IOUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.mapping.api.*;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+
+import javax.management.ObjectName;
+import javax.net.ssl.SSLContext;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class ConcurrentClientsTest {
+
+ private static final int CONCURRENCY = 16;
+ @Mock
+ private YangStoreService yangStoreService;
+ @Mock
+ private ConfigRegistryJMXClient jmxClient;
+
+ private final InetSocketAddress netconfAddress = new InetSocketAddress("127.0.0.1", 8303);
+
+ static final Logger logger = LoggerFactory.getLogger(ConcurrentClientsTest.class);
+
+ private DefaultCommitNotificationProducer commitNot;
+ private NetconfServerDispatcher dispatch;
+
+ @Before
+ public void setUp() throws Exception {
+ { // init mocks
+ MockitoAnnotations.initMocks(this);
+ final YangStoreSnapshot yStore = mock(YangStoreSnapshot.class);
+ doReturn(yStore).when(this.yangStoreService).getYangStoreSnapshot();
+ doReturn(Collections.emptyMap()).when(yStore).getModuleMXBeanEntryMap();
+ doReturn(Collections.emptyMap()).when(yStore).getModuleMap();
+
+ final ConfigTransactionJMXClient mockedTCl = mock(ConfigTransactionJMXClient.class);
+ doReturn(mockedTCl).when(this.jmxClient).getConfigTransactionClient(any(ObjectName.class));
+
+ doReturn(Collections.emptySet()).when(jmxClient).lookupConfigBeans();
+ }
+
+ NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+ factoriesListener.onAddNetconfOperationServiceFactory(mockOpF());
+
+ SessionIdProvider idProvider = new SessionIdProvider();
+ NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+ new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
+
+ commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+
+ NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+ factoriesListener, commitNot, idProvider);
+ dispatch = new NetconfServerDispatcher(Optional.<SSLContext> absent(), serverNegotiatorFactory, listenerFactory);
+
+ ChannelFuture s = dispatch.createServer(netconfAddress);
+ s.await();
+ }
+
+ private NetconfOperationServiceFactory mockOpF() {
+ return new NetconfOperationServiceFactory() {
+ @Override
+ public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ return new NetconfOperationService() {
+ @Override
+ public Set<Capability> getCapabilities() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return Sets.<NetconfOperation> newHashSet(new NetconfOperation() {
+ @Override
+ public HandlingPriority canHandle(Document message) {
+ return HandlingPriority.getHandlingPriority(Integer.MAX_VALUE);
+ }
+
+ @Override
+ public Document handle(Document message, NetconfOperationRouter operationRouter)
+ throws NetconfDocumentedException {
+ try {
+ return XmlUtil.readXmlToDocument("<test/>");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public Set<NetconfOperationFilter> getFilters() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void close() {
+ }
+ };
+ }
+ };
+ }
+
+ @After
+ public void cleanUp() throws Exception {
+ commitNot.close();
+ dispatch.close();
+ }
+
+ @Test
+ public void multipleClients() throws Exception {
+ List<TestingThread> threads = new ArrayList<>();
+
+ final int attempts = 5;
+ for (int i = 0; i < CONCURRENCY; i++) {
+ TestingThread thread = new TestingThread(String.valueOf(i), attempts);
+ threads.add(thread);
+ thread.start();
+ }
+
+ for (TestingThread thread : threads) {
+ thread.join();
+ assertTrue(thread.success);
+ }
+ }
+
+ @Test
+ public void synchronizationTest() throws Exception {
+ new BlockingThread("foo").run2();
+ }
+
+ @Test
+ public void multipleBlockingClients() throws Exception {
+ List<BlockingThread> threads = new ArrayList<>();
+ for (int i = 0; i < CONCURRENCY; i++) {
+ BlockingThread thread = new BlockingThread(String.valueOf(i));
+ threads.add(thread);
+ thread.start();
+ }
+
+ for (BlockingThread thread : threads) {
+ thread.join();
+ assertTrue(thread.success);
+ }
+ }
+
+ class BlockingThread extends Thread {
+ Boolean success;
+
+ public BlockingThread(String name) {
+ super("client-" + name);
+ }
+
+ @Override
+ public void run() {
+ try {
+ run2();
+ success = true;
+ } catch (Exception e) {
+ success = false;
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void run2() throws Exception {
+ InputStream clientHello = checkNotNull(XmlFileLoader
+ .getResourceAsStream("netconfMessages/client_hello.xml"));
+ InputStream getConfig = checkNotNull(XmlFileLoader.getResourceAsStream("netconfMessages/getConfig.xml"));
+
+ Socket clientSocket = new Socket(netconfAddress.getHostString(), netconfAddress.getPort());
+ DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
+ InputStreamReader inFromServer = new InputStreamReader(clientSocket.getInputStream());
+
+ StringBuffer sb = new StringBuffer();
+ while (sb.toString().endsWith("]]>]]>") == false) {
+ sb.append((char) inFromServer.read());
+ }
+ logger.info(sb.toString());
+
+ outToServer.write(IOUtils.toByteArray(clientHello));
+ outToServer.write("]]>]]>".getBytes());
+ outToServer.flush();
+ // Thread.sleep(100);
+ outToServer.write(IOUtils.toByteArray(getConfig));
+ outToServer.write("]]>]]>".getBytes());
+ outToServer.flush();
+ Thread.sleep(100);
+ sb = new StringBuffer();
+ while (sb.toString().endsWith("]]>]]>") == false) {
+ sb.append((char) inFromServer.read());
+ }
+ logger.info(sb.toString());
+ clientSocket.close();
+ }
+ }
+
+ class TestingThread extends Thread {
+
+ private final String clientId;
+ private final int attempts;
+ private Boolean success;
+
+ TestingThread(String clientId, int attempts) {
+ this.clientId = clientId;
+ this.attempts = attempts;
+ setName("client-" + clientId);
+ }
+
+ @Override
+ public void run() {
+ try {
+ final NetconfClient netconfClient = new NetconfClient(clientId, netconfAddress);
+ long sessionId = netconfClient.getSessionId();
+ logger.info("Client with sessionid {} hello exchanged", sessionId);
+
+ final NetconfMessage getMessage = XmlFileLoader
+ .xmlFileToNetconfMessage("netconfMessages/getConfig.xml");
+ NetconfMessage result = netconfClient.sendMessage(getMessage);
+ logger.info("Client with sessionid {} got result {}", sessionId, result);
+ netconfClient.close();
+ logger.info("Client with session id {} ended", sessionId);
+ success = true;
+ } catch (final Exception e) {
+ success = false;
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
--- /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.impl;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.util.messages.NetconfMessageHeader;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class MessageHeaderTest {
+
+ private NetconfMessageHeader header = null;
+
+ @Before
+ public void setUp() {
+ this.header = new NetconfMessageHeader();
+ }
+
+ @Test
+ public void testFromBytes() {
+ final byte[] raw = new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x35, (byte) 0x38, (byte) 0x0a };
+ this.header.fromBytes(raw);
+ assertEquals(58, this.header.getLength());
+ }
+
+ @Test
+ public void testToBytes() {
+ this.header.setLength(123);
+ assertArrayEquals(new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x0a },
+ this.header.toBytes());
+ }
+}
--- /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.impl;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.custommonkey.xmlunit.XMLAssert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.messages.FramingMechanism;
+import org.opendaylight.controller.netconf.util.messages.NetconfMessageFactory;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.DeserializerException;
+import org.opendaylight.protocol.framework.DocumentedException;
+import org.opendaylight.protocol.util.ByteArray;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+public class MessageParserTest {
+
+ private NetconfMessageFactory parser = null;
+
+ @Before
+ public void setUp() {
+ this.parser = new NetconfMessageFactory();
+ }
+
+ @Test
+ public void testPutEOM() throws IOException, SAXException, ParserConfigurationException {
+ final NetconfMessage msg = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/client_hello.xml");
+ final byte[] bytes = this.parser.put(msg);
+ assertArrayEquals(NetconfMessageFactory.endOfMessage, ByteArray.subByte(bytes, bytes.length
+ - NetconfMessageFactory.endOfMessage.length, NetconfMessageFactory.endOfMessage.length));
+ }
+
+ @Ignore
+ @Test
+ // TODO not working on WINDOWS
+ // arrays first differed at element [4]; expected:<49> but was:<53>
+ // at
+ // org.junit.internal.ComparisonCriteria.arrayEquals(ComparisonCriteria.java:52)
+ public void testPutChunk() throws IOException, SAXException, ParserConfigurationException {
+ final NetconfMessage msg = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/client_hello.xml");
+ this.parser.setFramingMechanism(FramingMechanism.CHUNK);
+ final byte[] bytes = this.parser.put(msg);
+ final byte[] header = new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x32, (byte) 0x31, (byte) 0x31, (byte) 0x0a };
+ assertArrayEquals(header, ByteArray.subByte(bytes, 0, header.length));
+ assertArrayEquals(NetconfMessageFactory.endOfChunk, ByteArray.subByte(bytes, bytes.length
+ - NetconfMessageFactory.endOfChunk.length, NetconfMessageFactory.endOfChunk.length));
+ }
+
+ @Test
+ public void testParseEOM() throws IOException, SAXException, DeserializerException, DocumentedException,
+ ParserConfigurationException {
+ final Document msg = XmlFileLoader.xmlFileToDocument("netconfMessages/client_hello.xml");
+ final byte[] bytes = this.parser.put(new NetconfMessage(msg));
+ final Document doc = this.parser
+ .parse(ByteArray.subByte(bytes, 0, bytes.length - NetconfMessageFactory.endOfMessage.length))
+ .iterator().next().getDocument();
+ assertEquals(XmlUtil.toString(msg), XmlUtil.toString(doc));
+ XMLAssert.assertXMLEqual(msg, doc);
+ }
+
+ @Test
+ public void testParseChunk() throws IOException, SAXException, DeserializerException, DocumentedException,
+ ParserConfigurationException {
+ final Document msg = XmlFileLoader.xmlFileToDocument("netconfMessages/client_hello.xml");
+ this.parser.setFramingMechanism(FramingMechanism.CHUNK);
+ final byte[] bytes = this.parser.put(new NetconfMessage(msg));
+ final Document doc = this.parser
+ .parse(ByteArray.subByte(bytes, 6, bytes.length - NetconfMessageFactory.endOfChunk.length - 6))
+ .iterator().next().getDocument();
+ assertEquals(XmlUtil.toString(msg), XmlUtil.toString(doc));
+ XMLAssert.assertXMLEqual(msg, doc);
+ }
+
+}
--- /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.impl;
+
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+
+import javax.net.ssl.SSLContext;
+
+import org.junit.Test;
+import org.opendaylight.controller.netconf.impl.*;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+
+import com.google.common.base.Optional;
+
+import io.netty.channel.ChannelFuture;
+import io.netty.util.HashedWheelTimer;
+
+public class NetconfDispatcherImplTest {
+
+ @Test
+ public void test() throws Exception {
+
+ DefaultCommitNotificationProducer commitNot = new DefaultCommitNotificationProducer(
+ ManagementFactory.getPlatformMBeanServer());
+ NetconfOperationServiceFactoryListener factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+
+ SessionIdProvider idProvider = new SessionIdProvider();
+ NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+ new HashedWheelTimer(), factoriesListener, idProvider);
+
+ NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+ factoriesListener, commitNot, idProvider);
+ NetconfServerDispatcher dispatch = new NetconfServerDispatcher(Optional.<SSLContext> absent(),
+ serverNegotiatorFactory, listenerFactory);
+
+ InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 8333);
+ ChannelFuture s = dispatch.createServer(addr);
+
+ commitNot.close();
+ dispatch.close();
+ }
+}
--- /dev/null
+
+#24
+<rpc message-id="101" xm
+#28
+lns="urn:ietf:params:xml:ns:
+#2
+ne
+#33
+tconf:base:1.0">
+ <my-own-method
+#3
+ xm
+#13
+lns="http://e
+#34
+xample.net/me/my-own/1.0">
+ <my
+#8
+-first-p
+#21
+arameter>14</my-first
+#26
+-parameter>
+ <another-p
+#23
+arameter>fred</another-
+#31
+parameter>
+ </my-own-method>
+ <
+#2
+/r
+#3
+pc>
+##
--- /dev/null
+
+#22
+<rpc message-id="101"
+#24
+xmlns="urn:ietf:params:x
+#15
+ml:ns:netconf:b
+#54
+ase:1.0">
+ <get-config>
+ <source>
+ <r
+#2
+un
+#9
+ning/>
+
+#18
+ </source> <fi
+#33
+lter type="subtree">
+ <top x
+#4
+mlns
+#31
+="http://example.com/schema/1.2
+#15
+/config">
+
+#19
+ <users>
+
+#8
+ <us
+#3
+er/
+#5
+>
+
+#77
+ </users>
+ </top>
+ </filter>
+ </g
+#17
+et-config>
+</rpc>
+##
--- /dev/null
+
+#43
+<rpc message-id="101" xmlns="urn:ietf:param
+#14
+s:xml:ns:netco
+#14
+nf:base:1.0">
+
+#26
+<get-config>
+ <source>
+
+#35
+ <running/>
+ </source>
+
+#39
+ <filter type="subtree">
+ <
+#40
+top xmlns="http://example.com/schema/1.2
+#26
+/config">
+ <users>
+
+#36
+ <user>
+ <name>f
+#56
+red</name>
+ </user>
+ </users>
+
+#28
+ </top>
+ </fil
+#1
+t
+#28
+er>
+ </get-config>
+ </rpc>
+##
--- /dev/null
+
+#17
+<rpc message-id="
+#25
+101" xmlns="urn:ietf:para
+#19
+ms:xml:ns:netconf:b
+#3
+ase
+#19
+:1.0">
+ <get-confi
+#61
+g>
+ <source>
+ <running/>
+ </source>
+
+#43
+ <filter type="subtree">
+ <!-- requ
+#13
+est a text ve
+#22
+rsion of the configura
+#9
+tion -->
+
+#16
+ <config-text
+#45
+xmlns="http://example.com/text/1.2/config"/>
+
+#22
+ </filter>
+ </
+#18
+get-config>
+</rpc>
+##
--- /dev/null
+
+#43
+<rpc message-id="101" xmlns="urn:ietf:param
+#41
+s:xml:ns:netconf:base:1.0">
+ <edit-confi
+#29
+g>
+ <target>
+ <ru
+#13
+nning/>
+ <
+#4
+/tar
+#18
+get>
+ <config>
+
+#41
+ <top xmlns="http://example.com/schema/1
+#32
+.2/config">
+ <interface>
+
+#29
+ <name>Ethernet0/0</na
+#30
+me>
+ <mtu>1500</mtu>
+
+#61
+</interface>
+ </top>
+ </config>
+ </e
+#9
+dit-confi
+#9
+g>
+</rpc>
+##
--- /dev/null
+curl http://localhost:17777/jolokia/read/org.opendaylight.controller:instanceName=fixed1,type=ConfigBean,interfaceName=testing-threadpool | jsonpp
+{
+ "request": {
+ "mbean": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean",
+ "type": "read"
+ },
+ "status": 200,
+ "timestamp": 1362416252,
+ "value": {
+ "ExportedInterfaces": [
+ "testing-threadpool",
+ "modifiable-threadpool"
+ ],
+ "ImplementationName": "fixed",
+ "ThreadCount": 10,
+ "TriggerNewInstanceCreation": false
+ }
+}
\ No newline at end of file
--- /dev/null
+$ curl 'http://localhost:17777/jolokia/exec/org.opendaylight.controller:type=ConfigRegistry/lookupConfigBeans()' | jsonpp
+{
+ "request": {
+ "mbean": "org.opendaylight.controller:type=ConfigRegistry",
+ "operation": "lookupConfigBeans()",
+ "type": "exec"
+ },
+ "status": 200,
+ "timestamp": 1362417043,
+ "value": [
+ {
+ "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=modifiable-threadpool,type=ConfigBean"
+ },
+ {
+ "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean"
+ }
+ ]
+}
--- /dev/null
+<rpc message-id="104"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <commit/>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="102"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <lock>
+ <target>
+ <candidate/>
+ </target>
+ </lock>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="101"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <lock>
+ <target>
+ <running/>
+ </target>
+ </lock>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="103"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>none</default-operation>
+ <test-option>test-then-set</test-option>
+ <error-option>stop-on-error</error-option>
+ <nc:config
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ xmlns="uri-for-my-data-model-namespace">
+ <some-existing-node>
+ <my-new-node nc:operation="create">
+ <my-new-leaf>7</my-new-leaf>
+ </my-new-node>
+ </some-existing-node>
+ </nc:config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="105"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <unlock>
+ <target>
+ <candidate/>
+ </target>
+ </unlock>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="106"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <unlock>
+ <target>
+ <running/>
+ </target>
+ </unlock>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <rpc-error>
+ <error-type>rpc</error-type>
+ <error-tag>missing-attribute</error-tag>
+ <error-severity>error</error-severity>
+ <error-info>
+ <bad-attribute>message-id</bad-attribute>
+ <bad-element>rpc</bad-element>
+ </error-info>
+ </rpc-error>
+</rpc-reply>
--- /dev/null
+{\r
+ "value":null,\r
+ "status":200,\r
+ "request": {\r
+ "type":"exec",\r
+ "mbean":"java.util.logging:type=Logging",\r
+ "operation":"setLoggerLevel",\r
+ "arguments":["global","INFO"]\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+<jmxbean>\r
+ <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+ <type>EXEC</type>\r
+ <operation>lookupConfigBeans</operation>\r
+ <arguments>abc,bcd.aas</arguments>\r
+ <arguments>64</arguments>\r
+</jmxbean>
\ No newline at end of file
--- /dev/null
+<jmxbean>\r
+ <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+ <type>EXEC</type>\r
+ <operation>lookupConfigBeans</operation>\r
+ <arguments>\r
+ <elements>22</elements>\r
+ <elements>69</elements>\r
+ </arguments>\r
+</jmxbean>
\ No newline at end of file
--- /dev/null
+<jmxbean>\r
+ <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+ <type>EXEC</type>\r
+ <operation>lookupConfigBeans</operation>\r
+ <arguments>\r
+ <map>\r
+ <topology-registry>single</topology-registry>\r
+ <bgp-update>mock</bgp-update>\r
+ </map>\r
+ <array>2</array>\r
+ <array>22</array>\r
+ <anotherArg>arg</anotherArg>\r
+ </arguments>\r
+</jmxbean>
\ No newline at end of file
--- /dev/null
+<config>\r
+ <jmxbean>\r
+ <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+ <type>EXEC</type>\r
+ <operation>lookupConfigBeans</operation>\r
+ <attribute>abc,bcd.aas</attribute>\r
+ <attribute>22</attribute>\r
+ </jmxbean>\r
+ <jmxbean>\r
+ <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+ <type>WRITE</type>\r
+ <attribute>attribute</attribute>\r
+ <value>22</value>\r
+ </jmxbean>\r
+ <jmxbean>\r
+ <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+ <type>EXEC</type>\r
+ <operation>lookupConfigBeans</operation>\r
+ <arguments>\r
+ <map>\r
+ <topology-registry>single</topology-registry>\r
+ <bgp-update>mock</bgp-update>\r
+ </map>\r
+ <array>2</array>\r
+ <array>22</array>\r
+ <anotherArg>arg</anotherArg>\r
+ </arguments>\r
+ </jmxbean>\r
+</config>
\ No newline at end of file
--- /dev/null
+<jmxbean>\r
+ <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+ <type>WRITE</type>\r
+ <attribute>attribute</attribute>\r
+ <value>22</value>\r
+</jmxbean>
\ No newline at end of file
--- /dev/null
+<jmxbean>\r
+ <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+ <type>WRITE</type>\r
+ <attribute>attribute</attribute>\r
+ <value>22</value>\r
+ <value>222</value>\r
+ <value>223</value>\r
+</jmxbean>
\ No newline at end of file
--- /dev/null
+<jmxbean>\r
+ <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+ <type>WRITE</type>\r
+ <attribute>setAtr</attribute>\r
+ <value>\r
+ <abc>1</abc>\r
+ <bcd>2</bcd>\r
+ </value>\r
+</jmxbean>\r
--- /dev/null
+{\r
+ "timestamp":1362488209,\r
+ "status":200,\r
+ "request":{\r
+ "mbean":"org.opendaylight.controller:type=ConfigRegistry",\r
+ "attribute":"AvailableInterfacesAndImplementations",\r
+ "type":"read"\r
+ },\r
+ "value":{\r
+ "topology-registry":[\r
+ "single"\r
+ ],\r
+ "bgp-update":[\r
+ "mock",\r
+ "bgp-impl"\r
+ ],\r
+ "positioning-service":[\r
+ "combine",\r
+ "onehop",\r
+ "ondemand",\r
+ "pxe",\r
+ "precompute"\r
+ ],\r
+ "serializer":[\r
+ "serializer-impl"\r
+ ],\r
+ "network-topology-factory":[\r
+ "mock-xml",\r
+ "bgp-network-topology-factory",\r
+ "transient"\r
+ ],\r
+ "dwe-topology":[\r
+ "ebgp",\r
+ "defaultmetric",\r
+ "igp",\r
+ "network"\r
+ ],\r
+ "thread-factory":[\r
+ "naming-thread-factory"\r
+ ],\r
+ "bgp-parser":[\r
+ "parser"\r
+ ],\r
+ "pcep-dispatcher":[\r
+ "dispatcher"\r
+ ],\r
+ "threadpool":[\r
+ "flexible",\r
+ "fixed",\r
+ "scheduled"\r
+ ],\r
+ "scheduled-threadpool":[\r
+ "scheduled"\r
+ ],\r
+ "positioning-onehop":[\r
+ "onehop"\r
+ ],\r
+ "bgp-dispatcher":[\r
+ "bgp-dispatcher-impl"\r
+ ],\r
+ "cost-combiner":[\r
+ "pxe"\r
+ ],\r
+ "apsp-provider":[\r
+ "jgrapht",\r
+ "parallel",\r
+ "single-threaded"\r
+ ],\r
+ "topology":[\r
+ "ebgp",\r
+ "defaultmetric",\r
+ "igp",\r
+ "network"\r
+ ],\r
+ "soap-resource":[\r
+ "positioning-adaptor-pxe"\r
+ ],\r
+ "database-provider-factory":[\r
+ "transient"\r
+ ],\r
+ "bgp-proposal-checker":[\r
+ "bgp-proposal-checker-impl"\r
+ ],\r
+ "bgp-proposal":[\r
+ "bgp-proposal-impl"\r
+ ],\r
+ "listenable-network-topology-factory":[\r
+ "transient"\r
+ ],\r
+ "event-bus":[\r
+ "sync",\r
+ "async"\r
+ ],\r
+ "topology-registry-provider":[\r
+ "single"\r
+ ],\r
+ "topology-provider-factory":[\r
+ "transient"\r
+ ],\r
+ "rest-resource":[\r
+ "topology-resource-holder",\r
+ "alto-resource-holder",\r
+ "topology-visual-holder",\r
+ "network-resource-holder",\r
+ "path-resource-holder"\r
+ ],\r
+ "listenable-database-provider-factory":[\r
+ "transient"\r
+ ],\r
+ "topology-validator":[\r
+ "accept-all",\r
+ "threshold"\r
+ ],\r
+ "replicator":[\r
+ "replicator-impl"\r
+ ],\r
+ "server":[\r
+ "soap",\r
+ "rest"\r
+ ],\r
+ "combiner-pxe":[\r
+ "pxe"\r
+ ],\r
+ "rest":[\r
+ "rest"\r
+ ],\r
+ "soap":[\r
+ "soap"\r
+ ],\r
+ "path-service":[\r
+ "cariden"\r
+ ]\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>netconf-it</artifactId>
+ <name>${project.artifactId}</name>
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-api</artifactId>
+ <version>${config.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-util</artifactId>
+ <version>${config.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-store-api</artifactId>
+ <version>${config.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>util</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-client</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-netconf-connector</artifactId>
+ <version>${project.version}</version>
+ <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>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-persister-impl</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-manager</artifactId>
+ <version>${config.version}</version>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-impl</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-store-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-store-impl</artifactId>
+ <version>${config.version}</version>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>logback-config</artifactId>
+ <version>0.2.1-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <forkCount>1</forkCount>
+ <reuseForks>false</reuseForks>
+ <perCoreThreadCount>false</perCoreThreadCount>
+ </configuration>
+ <executions>
+ <execution>
+ <id>default-test</id>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </execution>
+ <execution>
+ <id>integration-tests</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <configuration>
+ <includes>
+ <include>**/org/opendaylight/controller/netconf/it/*.java</include>
+ </includes>
+ <skip>false</skip>
+ </configuration>
+ </execution>
+ </executions>
+ </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.it;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import io.netty.channel.ChannelFuture;
+import io.netty.util.HashedWheelTimer;
+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.netconf.persist.impl.ConfigPersisterNotificationHandler;
+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.*;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.impl.*;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.util.SSLUtil;
+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.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+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 InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
+ private static final InetSocketAddress tlsAddress = new InetSocketAddress("127.0.0.1", 12024);
+
+ private NetconfMessage getConfig, getConfigCandidate, editConfig, closeSession;
+ private DefaultCommitNotificationProducer commitNot;
+ private NetconfServerDispatcher dispatch, dispatchS;
+
+ @Before
+ public void setUp() throws Exception {
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray(
+ new ModuleFactory[0])));
+
+ loadMessages();
+
+ NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+ factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore()));
+
+ commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+
+ dispatch = createDispatcher(Optional.<SSLContext> absent(), factoriesListener);
+ ChannelFuture s = dispatch.createServer(tcpAddress);
+ s.await();
+
+ dispatchS = createDispatcher(Optional.of(getSslContext()), factoriesListener);
+ s = dispatchS.createServer(tlsAddress);
+ s.await();
+ }
+
+ private NetconfServerDispatcher createDispatcher(Optional<SSLContext> sslC,
+ NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+ SessionIdProvider idProvider = new SessionIdProvider();
+ NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+ new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
+
+ NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+ factoriesListener, commitNot, idProvider);
+
+ return new NetconfServerDispatcher(sslC, serverNegotiatorFactory, listenerFactory);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ commitNot.close();
+ dispatch.close();
+ dispatchS.close();
+ }
+
+ private SSLContext getSslContext() throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
+ IOException, UnrecoverableKeyException, KeyManagementException {
+ final InputStream keyStore = getClass().getResourceAsStream("/keystore.jks");
+ final InputStream trustStore = getClass().getResourceAsStream("/keystore.jks");
+ SSLContext sslContext = SSLUtil.initializeSecureContext("password", keyStore, trustStore, "SunX509");
+ keyStore.close();
+ trustStore.close();
+ return sslContext;
+ }
+
+ private void loadMessages() throws IOException, SAXException, ParserConfigurationException {
+ this.editConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/edit_config.xml");
+ this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml");
+ this.getConfigCandidate = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig_candidate.xml");
+ this.closeSession = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/closeSession.xml");
+ }
+
+ private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException {
+ final Collection<InputStream> yangDependencies = getBasicYangs();
+ return new HardcodedYangStoreService(yangDependencies);
+ }
+
+ private 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/ietf-inet-types.yang");
+ final Collection<InputStream> yangDependencies = new ArrayList<>();
+ for (String path : paths) {
+ final InputStream is = checkNotNull(getClass().getResourceAsStream(path), path + " not found");
+ yangDependencies.add(is);
+ }
+ return yangDependencies;
+ }
+
+ protected List<ModuleFactory> getModuleFactories() {
+ return getModuleFactoriesS();
+ }
+
+ static List<ModuleFactory> getModuleFactoriesS() {
+ return Lists.newArrayList(new TestImplModuleFactory(), new DepTestImplModuleFactory(),
+ new NetconfTestImplModuleFactory());
+ }
+
+ @Test
+ public void testTwoSessions() throws Exception {
+ try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 4000)) {
+ try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 4000)) {
+ }
+ }
+ }
+
+ @Test(timeout = 10000)
+ public void testPersister() throws Exception {
+ Persister persister = mock(Persister.class);
+ doReturn("mockPersister").when(persister).toString();
+ doReturn(Optional.absent()).when(persister).loadLastConfig();
+ ConfigPersisterNotificationHandler h = new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer());
+ h.init();
+ }
+
+ @Test
+ public void testSecure() throws Exception {
+ try (NetconfClient netconfClient = new NetconfClient("1", tlsAddress, 4000, Optional.of(getSslContext()))) {
+
+ }
+ }
+
+ @Ignore
+ @Test
+ public void waitingTest() throws Exception {
+ final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
+ transaction.createModule(DepTestImplModuleFactory.NAME, "eb");
+ transaction.commit();
+ Thread.currentThread().suspend();
+ }
+
+ @Test
+ public void rpcReplyContainsAllAttributesTest() throws Exception {
+ try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+ final String rpc = "<rpc message-id=\"5\" a=\"a\" b=\"44\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<get/>" + "</rpc>";
+ final Document doc = XmlUtil.readXmlToDocument(rpc);
+ final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc));
+ assertNotNull(message);
+ final NamedNodeMap expectedAttributes = doc.getDocumentElement().getAttributes();
+ final NamedNodeMap returnedAttributes = message.getDocument().getDocumentElement().getAttributes();
+
+ assertSameAttributes(expectedAttributes, returnedAttributes);
+ }
+ }
+
+ private void assertSameAttributes(final NamedNodeMap expectedAttributes, final NamedNodeMap returnedAttributes) {
+ assertNotNull("Expecting 4 attributes", returnedAttributes);
+ assertEquals(expectedAttributes.getLength(), returnedAttributes.getLength());
+
+ for (int i = 0; i < expectedAttributes.getLength(); i++) {
+ final Node expAttr = expectedAttributes.item(i);
+ final Node attr = returnedAttributes.item(i);
+ assertEquals(expAttr.getNodeName(), attr.getNodeName());
+ assertEquals(expAttr.getNamespaceURI(), attr.getNamespaceURI());
+ assertEquals(expAttr.getTextContent(), attr.getTextContent());
+ }
+ }
+
+ @Test
+ public void rpcReplyErrorContainsAllAttributesTest() throws Exception {
+ try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+ final String rpc = "<rpc message-id=\"1\" a=\"adada\" b=\"4\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<commit/>" + "</rpc>";
+ final Document doc = XmlUtil.readXmlToDocument(rpc);
+ final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc));
+ final NamedNodeMap expectedAttributes = doc.getDocumentElement().getAttributes();
+ final NamedNodeMap returnedAttributes = message.getDocument().getDocumentElement().getAttributes();
+
+ assertSameAttributes(expectedAttributes, returnedAttributes);
+ }
+ }
+
+ @Test
+ public void rpcOutputContainsCorrectNamespace() throws Exception {
+ final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
+ ObjectName dep = transaction.createModule(DepTestImplModuleFactory.NAME, "instanceD");
+ ObjectName impl = transaction.createModule(NetconfTestImplModuleFactory.NAME, "instance");
+ NetconfTestImplModuleMXBean proxy = configRegistryClient
+ .newMXBeanProxy(impl, NetconfTestImplModuleMXBean.class);
+ proxy.setTestingDep(dep);
+ registerRuntimeBean();
+
+ transaction.commit();
+
+ try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+ final String expectedNamespace = "urn:opendaylight:params:xml:ns:yang:controller:test:impl";
+
+ final String rpc = "<rpc message-id=\"5\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<no-arg xmlns=\""
+ + expectedNamespace
+ + "\"> "
+ + "<context-instance>/data/modules/module[name='impl-netconf']/instance[name='instance']</context-instance>"
+ + "<arg1>argument1</arg1>" + "</no-arg>" + "</rpc>";
+ final Document doc = XmlUtil.readXmlToDocument(rpc);
+ final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc));
+
+ final Element rpcReply = message.getDocument().getDocumentElement();
+ final XmlElement resultElement = XmlElement.fromDomElement(rpcReply).getOnlyChildElement();
+ assertEquals("result", resultElement.getName());
+ final String namespace = resultElement.getNamespaceAttribute();
+ assertEquals(expectedNamespace, namespace);
+ }
+ }
+
+ 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
+ public void testCloseSession() throws Exception {
+ try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+
+ // edit config
+ Document rpcReply = netconfClient.sendMessage(this.editConfig).getDocument();
+ assertIsOK(rpcReply);
+
+ rpcReply = netconfClient.sendMessage(this.closeSession).getDocument();
+
+ assertIsOK(rpcReply);
+ }
+ }
+
+ @Test
+ public void testEditConfig() throws Exception {
+ try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+ // send edit_config.xml
+ final Document rpcReply = netconfClient.sendMessage(this.editConfig).getDocument();
+ assertIsOK(rpcReply);
+ }
+ }
+
+ @Test
+ public void testValidate() throws Exception {
+ try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+ // begin transaction
+ Document rpcReply = netconfClient.sendMessage(getConfigCandidate).getDocument();
+ assertEquals("data", XmlElement.fromDomDocument(rpcReply).getOnlyChildElement().getName());
+
+ // operations empty
+ rpcReply = netconfClient.sendMessage(XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/validate.xml"))
+ .getDocument();
+ assertIsOK(rpcReply);
+ }
+ }
+
+ private void assertIsOK(final Document rpcReply) {
+ assertEquals("rpc-reply", rpcReply.getDocumentElement().getTagName());
+ assertEquals("ok", XmlElement.fromDomDocument(rpcReply).getOnlyChildElement().getName());
+ }
+
+ @Ignore
+ @Test
+ // TODO can only send NetconfMessage - it must be valid xml
+ public void testClientHelloWithAuth() throws Exception {
+ final String fileName = "netconfMessages/client_hello_with_auth.xml";
+ // final InputStream resourceAsStream =
+ // AbstractListenerTest.class.getResourceAsStream(fileName);
+ // assertNotNull(resourceAsStream);
+ try (NetconfClient netconfClient = new NetconfClient("test", tcpAddress, 5000)) {
+ // IOUtils.copy(resourceAsStream, netconfClient.getStream());
+ // netconfClient.getOutputStream().write(NetconfMessageFactory.endOfMessage);
+ // server should not write anything back
+ // assertEquals(null, netconfClient.readMessage());
+ assertGetConfigWorks(netconfClient);
+ }
+ }
+
+ private Document assertGetConfigWorks(final NetconfClient netconfClient) throws InterruptedException {
+ return assertGetConfigWorks(netconfClient, this.getConfig);
+ }
+
+ private Document assertGetConfigWorks(final NetconfClient netconfClient, final NetconfMessage getConfigMessage)
+ throws InterruptedException {
+ final NetconfMessage rpcReply = netconfClient.sendMessage(getConfigMessage);
+ assertNotNull(rpcReply);
+ assertEquals("data", XmlElement.fromDomDocument(rpcReply.getDocument()).getOnlyChildElement().getName());
+ return rpcReply.getDocument();
+ }
+
+ @Test
+ public void testGetConfig() throws Exception {
+ try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+ assertGetConfigWorks(netconfClient);
+ }
+ }
+
+ @Test
+ public void createYangTestBasedOnYuma() throws Exception {
+ try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+ Document rpcReply = netconfClient.sendMessage(
+ XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/editConfig_merge_yang-test.xml"))
+ .getDocument();
+ assertEquals("rpc-reply", rpcReply.getDocumentElement().getTagName());
+ assertIsOK(rpcReply);
+ assertGetConfigWorks(netconfClient, this.getConfigCandidate);
+ rpcReply = netconfClient.sendMessage(XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/commit.xml"))
+ .getDocument();
+ assertIsOK(rpcReply);
+
+ final ObjectName on = new ObjectName(
+ "org.opendaylight.controller:instanceName=impl-dep-instance,type=Module,moduleFactoryName=impl-dep");
+ Set<ObjectName> cfgBeans = configRegistryClient.lookupConfigBeans();
+ assertEquals(cfgBeans, Sets.newHashSet(on));
+ }
+ }
+
+ private NetconfClient createSession(final InetSocketAddress address, final String expected) throws Exception {
+ final NetconfClient netconfClient = new NetconfClient("test " + address.toString(), address, 5000);
+
+ assertEquals(expected, Long.toString(netconfClient.getSessionId()));
+
+ return netconfClient;
+ }
+
+}
--- /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">
+
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>netconf-mapping-api</artifactId>
+ <name>${project.artifactId}</name>
+
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ </dependencies>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Private-Package>
+ </Private-Package>
+ <Import-Package>
+ com.google.common.base,
+ org.opendaylight.controller.netconf.api,
+ org.w3c.dom
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.controller.netconf.mapping.api,
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /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.mapping.api;
+
+import com.google.common.base.Optional;
+
+/**
+ * Contains capability URI announced by server hello message and optionally its
+ * corresponding yang schema that can be retrieved by get-schema rpc.
+ */
+public interface Capability {
+
+ public String getCapabilityUri();
+
+ public Optional<String> getModuleName();
+
+ public Optional<String> getRevision();
+
+ public Optional<String> getCapabilitySchema();
+}
--- /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.mapping.api;
+
+import com.google.common.base.Optional;
+
+public class HandlingPriority implements Comparable<HandlingPriority> {
+
+ public static final HandlingPriority CANNOT_HANDLE = new HandlingPriority();
+ public static final HandlingPriority HANDLE_WITH_DEFAULT_PRIORITY = new HandlingPriority(Integer.MIN_VALUE);
+ public static final HandlingPriority HANDLE_WITH_MAX_PRIORITY = new HandlingPriority(Integer.MAX_VALUE);
+
+ private Integer priority;
+
+ public static HandlingPriority getHandlingPriority(int priority) {
+ return new HandlingPriority(priority);
+ }
+
+ private HandlingPriority(int priority) {
+ this.priority = priority;
+ }
+
+ private HandlingPriority() {
+ }
+
+ /**
+ * @return priority number or Optional.absent otherwise
+ */
+ public Optional<Integer> getPriority() {
+ return Optional.of(priority).or(Optional.<Integer> absent());
+ }
+
+ // TODO test
+
+ @Override
+ public int compareTo(HandlingPriority o) {
+ if (this == o)
+ return 0;
+ if (this == CANNOT_HANDLE)
+ return -1;
+ if (o == CANNOT_HANDLE)
+ return 1;
+
+ if (priority > o.priority)
+ return 1;
+ if (priority == o.priority)
+ return 0;
+ if (priority < o.priority)
+ return -1;
+
+ throw new IllegalStateException("Unexpected state");
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof HandlingPriority))
+ return false;
+
+ HandlingPriority that = (HandlingPriority) o;
+
+ if (priority != null ? !priority.equals(that.priority) : that.priority != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return priority != null ? priority.hashCode() : 0;
+ }
+}
--- /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.mapping.api;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.w3c.dom.Document;
+
+public interface NetconfOperation {
+
+ HandlingPriority canHandle(Document message);
+
+ Document handle(Document message, NetconfOperationRouter operationRouter) throws NetconfDocumentedException;
+}
--- /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.mapping.api;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.w3c.dom.Document;
+
+/**
+ * Filters wrap each netconf operation, if there is one found. Filters are
+ * sorted and applied from the greatest to smallest sorting order.
+ */
+public interface NetconfOperationFilter extends Comparable<NetconfOperationFilter> {
+
+ Document doFilter(Document message, NetconfOperationRouter operationRouter, NetconfOperationFilterChain filterChain)
+ throws NetconfDocumentedException;
+
+ int getSoringOrder();
+
+}
--- /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.mapping.api;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.w3c.dom.Document;
+
+public interface NetconfOperationFilterChain {
+
+ Document execute(Document message, NetconfOperationRouter operationRouter) throws NetconfDocumentedException;
+
+}
--- /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.mapping.api;
+
+import java.util.Set;
+
+/**
+ *
+ */
+public interface NetconfOperationService extends AutoCloseable {
+
+ /**
+ * Get capabilities announced by server hello message.
+ */
+ Set<Capability> getCapabilities();
+
+ /**
+ * Get set of netconf operations that are handled by this service.
+ */
+ Set<NetconfOperation> getNetconfOperations();
+
+ Set<NetconfOperationFilter> getFilters();
+
+ /**
+ * Called when netconf session is destroyed.
+ */
+ @Override
+ 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.mapping.api;
+
+/**
+ * Factory that must be registered in OSGi service registry in order to be used
+ * by netconf-impl. Responsible for creating per-session instances of
+ * {@link NetconfOperationService}.
+ */
+public interface NetconfOperationServiceFactory {
+
+ NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting);
+
+}
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>netconf-util</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ <version>0.2.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>framework</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ org.opendaylight.controller.netconf.util,
+ org.opendaylight.controller.netconf.util.xml,
+ org.opendaylight.controller.netconf.util.osgi,
+ org.opendaylight.controller.netconf.util.mapping,
+ org.opendaylight.controller.netconf.util.messages,
+ </Export-Package>
+ <Import-Package>
+ org.opendaylight.controller.config.stat,
+ com.google.common.base,
+ com.google.common.collect,
+ io.netty.buffer,
+ io.netty.channel,
+ io.netty.channel.socket,
+ io.netty.handler.codec,
+ io.netty.handler.ssl,
+ io.netty.util,
+ io.netty.util.concurrent,
+ javax.annotation,
+ javax.net.ssl,
+ javax.xml.namespace,
+ javax.xml.parsers,
+ javax.xml.transform,
+ javax.xml.transform.dom,
+ javax.xml.transform.stream,
+ javax.xml.validation,
+ javax.xml.xpath,
+ org.opendaylight.controller.netconf.api,
+ org.opendaylight.controller.netconf.mapping.api,
+ org.opendaylight.protocol.framework,
+ org.opendaylight.protocol.util,
+ org.osgi.framework,
+ org.slf4j,
+ org.w3c.dom,
+ org.xml.sax,
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.4</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </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.util;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+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 com.google.common.base.Optional;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.util.concurrent.Promise;
+
+public abstract class AbstractChannelInitializer {
+
+ private final Optional<SSLContext> maybeContext;
+ private final NetconfHandlerFactory handlerFactory;
+
+ public AbstractChannelInitializer(Optional<SSLContext> maybeContext) {
+ this.maybeContext = maybeContext;
+ this.handlerFactory = new NetconfHandlerFactory(new NetconfMessageFactory());
+ }
+
+ public void initialize(SocketChannel ch, Promise<? extends NetconfSession> promise) {
+ if (maybeContext.isPresent()) {
+ initSsl(ch);
+ }
+
+ ch.pipeline().addLast("frameDecoder", NetconfMessageFactory.getDelimiterFrameDecoder());
+ ch.pipeline().addLast(handlerFactory.getDecoders());
+ initializeAfterDecoder(ch, promise);
+ ch.pipeline().addLast(handlerFactory.getEncoders());
+ }
+
+ protected abstract void initializeAfterDecoder(SocketChannel ch, Promise<? extends NetconfSession> promise);
+
+ private void initSsl(SocketChannel ch) {
+ SSLEngine sslEngine = maybeContext.get().createSSLEngine();
+ initSslEngine(sslEngine);
+ final SslHandler handler = new SslHandler(sslEngine);
+ ch.pipeline().addLast("ssl", handler);
+ }
+
+ protected abstract void initSslEngine(SSLEngine sslEngine);
+
+ 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) };
+ }
+ }
+}
--- /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.util;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import io.netty.channel.Channel;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.AbstractSessionNegotiator;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+
+import java.util.concurrent.TimeUnit;
+
+public abstract class AbstractNetconfSessionNegotiator<P extends NetconfSessionPreferences, S extends NetconfSession>
+ extends AbstractSessionNegotiator<NetconfMessage, S> {
+
+ // TODO what time ?
+ private static final long INITIAL_HOLDTIMER = 1;
+ private static final Logger logger = LoggerFactory.getLogger(AbstractNetconfSessionNegotiator.class);
+
+ protected final P sessionPreferences;
+
+ private final SessionListener sessionListener;
+
+ /**
+ * Possible states for Finite State Machine
+ */
+ private enum State {
+ IDLE, OPEN_WAIT, FAILED, ESTABLISHED
+ }
+
+ private State state = State.IDLE;
+ private final Timer timer;
+
+ protected AbstractNetconfSessionNegotiator(P sessionPreferences, Promise<S> promise, Channel channel, Timer timer,
+ SessionListener sessionListener) {
+ super(promise, channel);
+ this.sessionPreferences = sessionPreferences;
+ this.timer = timer;
+ this.sessionListener = sessionListener;
+ }
+
+ @Override
+ protected void startNegotiation() throws Exception {
+ final Optional<SslHandler> sslHandler = getSslHandler(channel);
+ if (sslHandler.isPresent()) {
+ Future<Channel> future = sslHandler.get().handshakeFuture();
+ future.addListener(new GenericFutureListener<Future<? super Channel>>() {
+ @Override
+ public void operationComplete(Future<? super Channel> future) throws Exception {
+ Preconditions.checkState(future.isSuccess(), "Ssl handshake was not successful");
+ logger.debug("Ssl handshake complete");
+ start();
+ }
+ });
+ } else
+ start();
+ }
+
+ private static Optional<SslHandler> getSslHandler(Channel channel) {
+ final SslHandler sslHandler = channel.pipeline().get(SslHandler.class);
+ return sslHandler == null ? Optional.<SslHandler> absent() : Optional.of(sslHandler);
+ }
+
+ private void start() {
+ final NetconfMessage helloMessage = this.sessionPreferences.getHelloMessage();
+ logger.debug("Session negotiation started with hello message {}", XmlUtil.toString(helloMessage.getDocument()));
+
+ sendMessage(helloMessage);
+ changeState(State.OPEN_WAIT);
+
+ this.timer.newTimeout(new TimerTask() {
+ @Override
+ public void run(final Timeout timeout) throws Exception {
+ synchronized (this) {
+ if (state != State.ESTABLISHED) {
+ final IllegalStateException cause = new IllegalStateException(
+ "Session was not established after " + timeout);
+ negotiationFailed(cause);
+ changeState(State.FAILED);
+ }
+ }
+ }
+ }, INITIAL_HOLDTIMER, TimeUnit.MINUTES);
+ }
+
+ private void sendMessage(NetconfMessage message) {
+ this.channel.writeAndFlush(message);
+ }
+
+ @Override
+ protected void handleMessage(NetconfMessage netconfMessage) {
+ final Document doc = netconfMessage.getDocument();
+
+ if (isHelloMessage(doc)) {
+ changeState(State.ESTABLISHED);
+ S session = getSession(sessionListener, channel, doc);
+ negotiationSuccessful(session);
+ } else {
+ final IllegalStateException cause = new IllegalStateException(
+ "Received message was not hello as expected, but was " + XmlUtil.toString(doc));
+ negotiationFailed(cause);
+ }
+ }
+
+ protected abstract S getSession(SessionListener sessionListener, Channel channel, Document doc);
+
+ private boolean isHelloMessage(Document doc) {
+ try {
+ XmlElement.fromDomElementWithExpected(doc.getDocumentElement(), "hello",
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private void changeState(final State newState) {
+ logger.debug("Changing state from : {} to : {}", state, newState);
+ Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s", state,
+ newState);
+ this.state = newState;
+ }
+
+ private static boolean isStateChangePermitted(State state, State newState) {
+ if (state == State.IDLE && newState == State.OPEN_WAIT)
+ return true;
+ if (state == State.OPEN_WAIT && newState == State.ESTABLISHED)
+ return true;
+ if (state == State.OPEN_WAIT && newState == State.FAILED)
+ return true;
+
+ return false;
+ }
+}
--- /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.util.mapping;
+
+import java.util.Map;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public abstract class AbstractNetconfOperation implements NetconfOperation {
+ private final String netconfSessionIdForReporting;
+
+ protected AbstractNetconfOperation(String netconfSessionIdForReporting) {
+ this.netconfSessionIdForReporting = netconfSessionIdForReporting;
+ }
+
+ public String getNetconfSessionIdForReporting() {
+ return netconfSessionIdForReporting;
+ }
+
+ @Override
+ public HandlingPriority canHandle(Document message) {
+ OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(message);
+ return canHandle(operationNameAndNamespace.getOperationName(), operationNameAndNamespace.getNamespace());
+ }
+
+ public static class OperationNameAndNamespace {
+ private final String operationName, namespace;
+
+ public OperationNameAndNamespace(Document message) {
+ XmlElement requestElement = getRequestElementWithCheck(message);
+
+ XmlElement operationElement = requestElement.getOnlyChildElement();
+ operationName = operationElement.getName();
+ namespace = operationElement.getNamespace();
+ }
+
+ public String getOperationName() {
+ return operationName;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+ }
+
+ protected static XmlElement getRequestElementWithCheck(Document message) {
+ return XmlElement.fromDomElementWithExpected(message.getDocumentElement(), XmlNetconfConstants.RPC_KEY,
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ }
+
+ protected abstract HandlingPriority canHandle(String operationName, String netconfOperationNamespace);
+
+ @Override
+ public Document handle(Document message, NetconfOperationRouter opRouter) throws NetconfDocumentedException {
+
+ XmlElement requestElement = getRequestElementWithCheck(message);
+
+ Document document = XmlUtil.newDocument();
+
+ XmlElement operationElement = requestElement.getOnlyChildElement();
+ Map<String, Attr> attributes = requestElement.getAttributes();
+
+ Element response = handle(document, operationElement, opRouter);
+
+ Element rpcReply = document.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0,
+ XmlNetconfConstants.RPC_REPLY_KEY);
+ rpcReply.appendChild(response);
+
+ for (String attrName : attributes.keySet()) {
+ rpcReply.setAttribute(attrName, attributes.get(attrName).getNodeValue());
+ }
+
+ document.appendChild(rpcReply);
+ return document;
+ }
+
+ protected abstract Element handle(Document document, XmlElement operationElement, NetconfOperationRouter opRouter)
+ throws NetconfDocumentedException;
+
+ @Override
+ public String toString() {
+ return getClass() + "{" + netconfSessionIdForReporting + '}';
+ }
+}
--- /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.util.messages;
+
+/**
+ * Known NETCONF framing mechanisms.
+ */
+public enum FramingMechanism {
+ /**
+ * @see <a href="http://tools.ietf.org/html/rfc6242#section-4.2">Chunked
+ * framing mechanism</a>
+ */
+ CHUNK,
+ /**
+ * @see <a
+ * href="http://tools.ietf.org/html/rfc6242#section-4.3">End-of-message
+ * framing mechanism</a>
+ */
+ EOM
+}
--- /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.util.messages;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandler;
+import io.netty.handler.codec.DelimiterBasedFrameDecoder;
+import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.DeserializerException;
+import org.opendaylight.protocol.framework.DocumentedException;
+import org.opendaylight.protocol.framework.ProtocolMessageFactory;
+import org.opendaylight.protocol.util.ByteArray;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * NetconfMessageFactory for (de)serializing DOM documents.
+ */
+public final class NetconfMessageFactory implements ProtocolMessageFactory<NetconfMessage> {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfMessageFactory.class);
+
+ public static final byte[] endOfMessage = "]]>]]>".getBytes(Charsets.UTF_8);
+
+ public static final byte[] endOfChunk = "\n##\n".getBytes(Charsets.UTF_8);
+
+ private static final int MAX_CHUNK_SIZE = 1024; // Bytes
+
+ private FramingMechanism framing = FramingMechanism.EOM;
+
+ private final Optional<String> clientId;
+
+ public NetconfMessageFactory() {
+ clientId = Optional.absent();
+ }
+
+ public NetconfMessageFactory(Optional<String> clientId) {
+ this.clientId = clientId;
+ }
+
+ public static ChannelHandler getDelimiterFrameDecoder() {
+ return new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer(endOfMessage));
+ }
+
+ @Override
+ public List<NetconfMessage> parse(byte[] bytes) throws DeserializerException, DocumentedException {
+ String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+ logger.debug("Parsing message \n{}", s);
+ if (bytes[0] == '[') {
+ // yuma sends auth information in the first line. Ignore until ]\n
+ // is found.
+ int endOfAuthHeader = ByteArray.findByteSequence(bytes, new byte[] { ']', '\n' });
+ if (endOfAuthHeader > -1) {
+ bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length);
+ }
+ }
+ List<NetconfMessage> messages = Lists.newArrayList();
+ try {
+ Document doc = XmlUtil.readXmlToDocument(new ByteArrayInputStream(bytes));
+ messages.add(new NetconfMessage(doc));
+ } catch (final SAXException | IOException | IllegalStateException e) {
+ throw new NetconfDeserializerException("Could not parse message from " + new String(bytes), e);
+ }
+ return messages;
+ }
+
+ @Override
+ public byte[] put(NetconfMessage netconfMessage) {
+ if (clientId.isPresent()) {
+ Comment comment = netconfMessage.getDocument().createComment("clientId:" + clientId.get());
+ netconfMessage.getDocument().appendChild(comment);
+ }
+ byte[] bytes = (this.framing == FramingMechanism.EOM) ? this.putEOM(netconfMessage) : this
+ .putChunked(netconfMessage);
+ String content = xmlToString(netconfMessage.getDocument());
+
+ logger.trace("Putting message \n{}", content);
+ return bytes;
+ }
+
+ private byte[] putEOM(NetconfMessage msg) {
+ // create byte buffer from the String XML
+ // all Netconf messages are encoded using UTF-8
+ final ByteBuffer msgBytes = Charsets.UTF_8.encode(xmlToString(msg.getDocument()));
+ final ByteBuffer result = ByteBuffer.allocate(msgBytes.limit() + endOfMessage.length);
+ result.put(msgBytes);
+ // put end of message
+ result.put(endOfMessage);
+ return result.array();
+ }
+
+ private byte[] putChunked(NetconfMessage msg) {
+ final ByteBuffer msgBytes = Charsets.UTF_8.encode(xmlToString(msg.getDocument()));
+ final NetconfMessageHeader h = new NetconfMessageHeader();
+ if (msgBytes.limit() > MAX_CHUNK_SIZE)
+ logger.warn("Netconf message too long, should be split.");
+ h.setLength(msgBytes.limit());
+ final byte[] headerBytes = h.toBytes();
+ final ByteBuffer result = ByteBuffer.allocate(headerBytes.length + msgBytes.limit() + endOfChunk.length);
+ result.put(headerBytes);
+ result.put(msgBytes);
+ result.put(endOfChunk);
+ return result.array();
+ }
+
+ private String xmlToString(Document doc) {
+ return XmlUtil.toString(doc, false);
+ }
+
+ /**
+ * For Hello message the framing is always EOM, but the framing mechanism
+ * may change.
+ *
+ * @param fm
+ * new framing mechanism
+ */
+ public void setFramingMechanism(final FramingMechanism fm) {
+ logger.debug("Framing mechanism changed to {}", fm);
+ this.framing = fm;
+ }
+}
--- /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.util.messages;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+import org.opendaylight.protocol.util.ByteArray;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Netconf message header is used only when chunked framing mechanism is
+ * supported. The header consists of only the length field.
+ */
+public final class NetconfMessageHeader {
+
+ private long length;
+
+ // \n#<length>\n
+ private static final byte[] headerBegin = new byte[] { (byte) 0x0a, (byte) 0x23 };
+
+ private static final byte headerEnd = (byte) 0x0a;
+
+ public static final int MIN_HEADER_LENGTH = 4; // bytes
+
+ public static final int MAX_HEADER_LENGTH = 13; // bytes
+
+ private boolean parsed = false;
+
+ public NetconfMessageHeader() {
+
+ }
+
+ public NetconfMessageHeader fromBytes(final byte[] bytes) {
+ // the length is variable therefore bytes between headerBegin and
+ // headerEnd mark the length
+ // the length should be only numbers and therefore easily parsed with
+ // ASCII
+ this.length = Long.parseLong(Charsets.US_ASCII.decode(
+ ByteBuffer.wrap(ByteArray.subByte(bytes, headerBegin.length, bytes.length - headerBegin.length - 1)))
+ .toString());
+ Preconditions.checkState(this.length < Integer.MAX_VALUE && this.length > 0);
+ this.parsed = true;
+ return this;
+ }
+
+ public byte[] toBytes() {
+ final byte[] l = String.valueOf(this.length).getBytes(Charsets.US_ASCII);
+ final byte[] h = new byte[headerBegin.length + l.length + 1];
+ System.arraycopy(headerBegin, 0, h, 0, headerBegin.length);
+ System.arraycopy(l, 0, h, headerBegin.length, l.length);
+ System.arraycopy(new byte[] { headerEnd }, 0, h, headerBegin.length + l.length, 1);
+ return h;
+ }
+
+ // FIXME: improve precision to long
+ public int getLength() {
+ return (int) this.length;
+ }
+
+ public void setLength(final int length) {
+ this.length = length;
+ }
+
+ /**
+ * @return the parsed
+ */
+ public boolean isParsed() {
+ return this.parsed;
+ }
+
+ /**
+ * @param parsed
+ * the parsed to set
+ */
+ public void setParsed() {
+ this.parsed = false;
+ }
+}
--- /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.util.messages;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.w3c.dom.Document;
+
+public class NetconfMessageUtil {
+
+ public static boolean isOKMessage(NetconfMessage message) {
+ return isOKMessage(message.getDocument());
+ }
+
+ public static boolean isOKMessage(Document document) {
+ return isOKMessage(XmlElement.fromDomDocument(document));
+ }
+
+ public static boolean isOKMessage(XmlElement xmlElement) {
+ return xmlElement.getOnlyChildElement().getName().equals(XmlNetconfConstants.OK);
+ }
+
+ public static boolean isErrorMEssage(NetconfMessage message) {
+ return isErrorMessage(message.getDocument());
+ }
+
+ public static boolean isErrorMessage(Document document) {
+ return isErrorMessage(XmlElement.fromDomDocument(document));
+ }
+
+ public static boolean isErrorMessage(XmlElement xmlElement) {
+ return xmlElement.getOnlyChildElement().getName().equals(XmlNetconfConstants.RPC_ERROR);
+
+ }
+}
--- /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.util.messages;
+
+import com.google.common.base.Preconditions;
+import io.netty.channel.Channel;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.*;
+
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import java.io.InputStream;
+import java.util.Map.Entry;
+
+public class SendErrorExceptionUtil {
+ private static final Logger logger = LoggerFactory.getLogger(SendErrorExceptionUtil.class);
+
+ public static void sendErrorMessage(final NetconfSession session,
+ final NetconfDocumentedException sendErrorException) {
+ logger.info("Sending error {}", sendErrorException.getMessage(), sendErrorException);
+ final Document errorDocument = createDocument(sendErrorException);
+ session.sendMessage(new NetconfMessage(errorDocument));
+ }
+
+ public static void sendErrorMessage(Channel channel, NetconfDocumentedException sendErrorException) {
+ logger.info("Sending error {}", sendErrorException.getMessage(), sendErrorException);
+ final Document errorDocument = createDocument(sendErrorException);
+ channel.writeAndFlush(new NetconfMessage(errorDocument));
+ }
+
+ public static void sendErrorMessage(NetconfSession session, NetconfDocumentedException sendErrorException,
+ NetconfMessage incommingMessage) {
+ final Document errorDocument = createDocument(sendErrorException);
+ logger.info("Sending error {}", XmlUtil.toString(errorDocument));
+ tryToCopyAttributes(incommingMessage.getDocument(), errorDocument, sendErrorException);
+ session.sendMessage(new NetconfMessage(errorDocument));
+ }
+
+ private static void tryToCopyAttributes(final Document incommingDocument, final Document errorDocument,
+ final NetconfDocumentedException sendErrorException) {
+ try {
+ final Element incommingRpc = incommingDocument.getDocumentElement();
+ Preconditions.checkState(incommingRpc.getTagName().equals(XmlNetconfConstants.RPC_KEY), "Missing "
+ + XmlNetconfConstants.RPC_KEY + " " + "element");
+
+ final Element rpcReply = errorDocument.getDocumentElement();
+ Preconditions.checkState(rpcReply.getTagName().equals(XmlNetconfConstants.RPC_REPLY_KEY), "Missing "
+ + XmlNetconfConstants.RPC_REPLY_KEY + " element");
+
+ final NamedNodeMap incomingAttributes = incommingRpc.getAttributes();
+ for (int i = 0; i < incomingAttributes.getLength(); i++) {
+ final Attr attr = (Attr) incomingAttributes.item(i);
+ // skip namespace
+ if (attr.getNodeName().equals(XmlUtil.XMLNS_ATTRIBUTE_KEY))
+ continue;
+ rpcReply.setAttributeNode((Attr) errorDocument.importNode(attr, true));
+ }
+ } catch (final Exception e) {
+ logger.warn("Unable to copy incomming attributes to {}, returned rpc-error might be invalid for client",
+ sendErrorException, e);
+ }
+ }
+
+ private static XPathExpression rpcErrorExpression = XMLNetconfUtil
+ .compileXPath("/netconf:rpc-reply/netconf:rpc-error");
+ private static XPathExpression errorTypeExpression = XMLNetconfUtil.compileXPath("netconf:error-type");
+ private static XPathExpression errorTagExpression = XMLNetconfUtil.compileXPath("netconf:error-tag");
+ private static XPathExpression errorSeverityExpression = XMLNetconfUtil.compileXPath("netconf:error-severity");
+
+ private static Document createDocument(final NetconfDocumentedException sendErrorException) {
+
+ final InputStream errIS = SendErrorExceptionUtil.class.getResourceAsStream("server_error.xml");
+ Document originalErrorDocument;
+ try {
+ originalErrorDocument = XmlUtil.readXmlToDocument(errIS);
+ } catch (final Exception e) {
+ throw new IllegalStateException(e);
+ }
+
+ final Document errorDocument = XmlUtil.createDocumentCopy(originalErrorDocument);
+ final Node rootNode = errorDocument.getFirstChild();
+
+ final Node rpcErrorNode = (Node) XmlUtil.evaluateXPath(rpcErrorExpression, rootNode, XPathConstants.NODE);
+
+ final Node errorTypeNode = (Node) XmlUtil.evaluateXPath(errorTypeExpression, rpcErrorNode, XPathConstants.NODE);
+ errorTypeNode.setTextContent(sendErrorException.getErrorType().getTagValue());
+
+ final Node errorTagNode = (Node) XmlUtil.evaluateXPath(errorTagExpression, rpcErrorNode, XPathConstants.NODE);
+ errorTagNode.setTextContent(sendErrorException.getErrorTag().getTagValue());
+
+ final Node errorSeverityNode = (Node) XmlUtil.evaluateXPath(errorSeverityExpression, rpcErrorNode,
+ XPathConstants.NODE);
+ errorSeverityNode.setTextContent(sendErrorException.getErrorSeverity().getTagValue());
+
+ if (sendErrorException.getErrorInfo() != null && sendErrorException.getErrorInfo().isEmpty() == false) {
+ /*
+ * <error-info> <bad-attribute>message-id</bad-attribute>
+ * <bad-element>rpc</bad-element> </error-info>
+ */
+ final Node errorInfoNode = errorDocument.createElementNS(
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, "error-info");
+
+ errorInfoNode.setPrefix(rootNode.getPrefix());
+ rpcErrorNode.appendChild(errorInfoNode);
+ for (final Entry<String, String> errorInfoEntry : sendErrorException.getErrorInfo().entrySet()) {
+ final Node node = errorDocument.createElementNS(
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, errorInfoEntry.getKey());
+ node.setTextContent(errorInfoEntry.getValue());
+ errorInfoNode.appendChild(node);
+ }
+
+ }
+ return errorDocument;
+ }
+
+}
--- /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.util.osgi;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+import org.opendaylight.protocol.util.SSLUtil;
+
+import javax.net.ssl.SSLContext;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+public class NetconfConfigUtil {
+ private static final String PREFIX_PROP = "netconf.";
+
+ private enum InfixProp {
+ tcp, tls
+ }
+
+ private static final String PORT_SUFFIX_PROP = ".port";
+ private static final String ADDRESS_SUFFIX_PROP = ".address";
+
+ 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(ConfigProvider configProvider) {
+ return extractSomeNetconfAddress(configProvider, InfixProp.tcp);
+ }
+
+ public static Optional<TLSConfiguration> extractTLSConfiguration(ConfigProvider configProvider) {
+ Optional<InetSocketAddress> address = extractSomeNetconfAddress(configProvider, InfixProp.tls);
+ if (address.isPresent()) {
+ String keystoreFileName = configProvider.getProperty(NETCONF_TLS_KEYSTORE_PROP);
+ File keystoreFile = new File(keystoreFileName);
+ checkState(keystoreFile.exists() && keystoreFile.isFile() && keystoreFile.canRead(),
+ "Keystore file %s does not exist or is not readable file", keystoreFileName);
+ keystoreFile = keystoreFile.getAbsoluteFile();
+ String keystorePassword = configProvider.getProperty(NETCONF_TLS_KEYSTORE_PASSWORD_PROP);
+ checkNotNull(keystoreFileName, "Property %s must be defined for tls netconf server",
+ NETCONF_TLS_KEYSTORE_PROP);
+ keystorePassword = keystorePassword != null ? keystorePassword : "";
+ return Optional.of(new TLSConfiguration(address.get(), keystoreFile, keystorePassword));
+ } else {
+ return Optional.absent();
+ }
+ }
+
+ public static class TLSConfiguration {
+ private final InetSocketAddress address;
+ private final File keystoreFile;
+ private final String keystorePassword;
+ private final SSLContext sslContext;
+
+ TLSConfiguration(InetSocketAddress address, File keystoreFile, String keystorePassword) {
+ this.address = address;
+ this.keystoreFile = keystoreFile;
+ this.keystorePassword = keystorePassword;
+ try {
+ try (InputStream keyStoreIS = new FileInputStream(keystoreFile)) {
+ try (InputStream trustStoreIS = new FileInputStream(keystoreFile)) {
+ sslContext = SSLUtil.initializeSecureContext("password", keyStoreIS, trustStoreIS, "SunX509");
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot initialize ssl context for netconf file " + keystoreFile, e);
+ }
+ }
+
+ public SSLContext getSslContext() {
+ return sslContext;
+ }
+
+ public InetSocketAddress getAddress() {
+ return address;
+ }
+
+ public File getKeystoreFile() {
+ return keystoreFile;
+ }
+
+ public String getKeystorePassword() {
+ return keystorePassword;
+ }
+ }
+
+ /**
+ * @param configProvider
+ * from which properties are being read.
+ * @param infixProp
+ * either tcp or tls
+ * @return absent if address is missing, value if address and port are
+ * valid.
+ * @throws IllegalStateException
+ * if address or port are invalid
+ */
+ private static Optional<InetSocketAddress> extractSomeNetconfAddress(ConfigProvider configProvider,
+ InfixProp infixProp) {
+ String address = configProvider.getProperty(PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP);
+ if (address == null) {
+ return Optional.absent();
+ }
+ String portKey = PREFIX_PROP + infixProp + PORT_SUFFIX_PROP;
+ String portString = configProvider.getProperty(portKey);
+ checkNotNull(portString, "Netconf port must be specified in properties file with " + portKey);
+ try {
+ int port = Integer.valueOf(portString);
+ return Optional.of(new InetSocketAddress(address, port));
+ } catch (RuntimeException e) {
+ throw new IllegalStateException("Cannot create " + infixProp + " netconf address from address:" + address
+ + " and port:" + portString, e);
+ }
+ }
+}
--- /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.util.xml;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.namespace.NamespaceContext;
+
+import com.google.common.collect.ImmutableMap;
+
+// http://www.ibm.com/developerworks/library/x-nmspccontext/
+public class HardcodedNamespaceResolver implements NamespaceContext {
+ private final Map<String/* prefix */, String/* namespace */> prefixesToNamespaces;
+
+ public HardcodedNamespaceResolver(String prefix, String namespace) {
+ this(ImmutableMap.of(prefix, namespace));
+ }
+
+ public HardcodedNamespaceResolver(Map<String, String> prefixesToNamespaces) {
+ this.prefixesToNamespaces = Collections.unmodifiableMap(prefixesToNamespaces);
+ }
+
+ /**
+ * This method returns the uri for all prefixes needed. Wherever possible it
+ * uses XMLConstants.
+ *
+ * @param prefix
+ * @return uri
+ */
+ @Override
+ public String getNamespaceURI(String prefix) {
+ if (prefixesToNamespaces.containsKey(prefix)) {
+ return prefixesToNamespaces.get(prefix);
+ } else {
+ throw new IllegalStateException("Prefix mapping not found for " + prefix);
+ }
+ }
+
+ @Override
+ public String getPrefix(String namespaceURI) {
+ // Not needed in this context.
+ return null;
+ }
+
+ @Override
+ public Iterator<?> getPrefixes(String namespaceURI) {
+ // Not needed in this context.
+ return null;
+ }
+
+}
--- /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.util.xml;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+public class XMLNetconfUtil {
+
+ public static XPathExpression compileXPath(String xPath) {
+ final XPathFactory xPathfactory = XPathFactory.newInstance();
+ final XPath xpath = xPathfactory.newXPath();
+ xpath.setNamespaceContext(new HardcodedNamespaceResolver("netconf",
+ XmlNetconfConstants.RFC4741_TARGET_NAMESPACE));
+ try {
+ return xpath.compile(xPath);
+ } catch (final XPathExpressionException e) {
+ throw new IllegalStateException("Error while compiling xpath expression " + xPath, e);
+ }
+ }
+
+}
--- /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.util.xml;
+
+import java.io.IOException;
+import java.util.*;
+
+import javax.annotation.Nullable;
+
+import org.w3c.dom.*;
+import org.xml.sax.SAXException;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+public class XmlElement {
+
+ public final Element element;
+
+ private XmlElement(Element element) {
+ this.element = element;
+ }
+
+ public static XmlElement fromDomElement(Element e) {
+ return new XmlElement(e);
+ }
+
+ public static XmlElement fromDomDocument(Document xml) {
+ return new XmlElement(xml.getDocumentElement());
+ }
+
+ public static XmlElement fromString(String s) {
+ try {
+ return new XmlElement(XmlUtil.readXmlToElement(s));
+ } catch (IOException | SAXException e) {
+ throw new IllegalArgumentException("Unable to create from " + s, e);
+ }
+ }
+
+ public static XmlElement fromDomElementWithExpected(Element element, String expectedName) {
+ XmlElement xmlElement = XmlElement.fromDomElement(element);
+ xmlElement.checkName(expectedName);
+ return xmlElement;
+ }
+
+ public static XmlElement fromDomElementWithExpected(Element element, String expectedName, String expectedNamespace) {
+ XmlElement xmlElement = XmlElement.fromDomElementWithExpected(element, expectedName);
+ xmlElement.checkNamespace(expectedNamespace);
+ return xmlElement;
+ }
+
+ private static Map<String, String> extractNamespaces(Element typeElement) {
+ Map<String, String> namespaces = new HashMap<>();
+ NamedNodeMap attributes = typeElement.getAttributes();
+ for (int i = 0; i < attributes.getLength(); i++) {
+ Node attribute = attributes.item(i);
+ String attribKey = attribute.getNodeName();
+ if (attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
+ String prefix;
+ if (attribKey.equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
+ prefix = "";
+ } else {
+ Preconditions.checkState(attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY + ":"));
+ prefix = attribKey.substring(XmlUtil.XMLNS_ATTRIBUTE_KEY.length() + 1);
+ }
+ namespaces.put(prefix, attribute.getNodeValue());
+ }
+ }
+ return namespaces;
+ }
+
+ public void checkName(String expectedName) {
+ Preconditions.checkArgument(getName().equals(expectedName), "Expected %s xml element but was %s", expectedName,
+ getName());
+ }
+
+ public void checkNamespaceAttribute(String expectedNamespace) {
+ Preconditions.checkArgument(getNamespaceAttribute().equals(expectedNamespace),
+ "Unexpected namespace %s for element %s, should be %s", getNamespaceAttribute(), expectedNamespace);
+ }
+
+ public void checkNamespace(String expectedNamespace) {
+ Preconditions.checkArgument(getNamespace().equals(expectedNamespace),
+ "Unexpected namespace %s for element %s, should be %s", getNamespace(), expectedNamespace);
+ }
+
+ public String getName() {
+ return element.getTagName();
+ }
+
+ public String getAttribute(String attributeName) {
+ return element.getAttribute(attributeName);
+ }
+
+ public String getAttribute(String attributeName, String namespace) {
+ return element.getAttributeNS(namespace, attributeName);
+ }
+
+ public void appendChild(Element element) {
+ this.element.appendChild(element);
+ // Element newElement = (Element) element.cloneNode(true);
+ // newElement.appendChild(configElement);
+ // return XmlElement.fromDomElement(newElement);
+ }
+
+ public Element getDomElement() {
+ return element;
+ }
+
+ public Map<String, Attr> getAttributes() {
+
+ Map<String, Attr> mappedAttributes = Maps.newHashMap();
+
+ NamedNodeMap attributes = element.getAttributes();
+ for (int i = 0; i < attributes.getLength(); i++) {
+ Attr attr = (Attr) attributes.item(i);
+ mappedAttributes.put(attr.getNodeName(), attr);
+ }
+
+ return mappedAttributes;
+ }
+
+ /**
+ * Non recursive
+ */
+ private List<XmlElement> getChildElementsInternal(ElementFilteringStrategy strat) {
+ NodeList childNodes = element.getChildNodes();
+ final List<XmlElement> result = new ArrayList<>();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node item = childNodes.item(i);
+ if (item instanceof Element == false)
+ continue;
+ if (strat.accept((Element) item))
+ result.add(new XmlElement((Element) item));
+ }
+
+ return result;
+ }
+
+ public List<XmlElement> getChildElements() {
+ return getChildElementsInternal(new ElementFilteringStrategy() {
+ @Override
+ public boolean accept(Element e) {
+ return true;
+ }
+ });
+ }
+
+ public List<XmlElement> getChildElementsWithinNamespace(final String childName, String namespace) {
+ return Lists.newArrayList(Collections2.filter(getChildElementsWithinNamespace(namespace),
+ new Predicate<XmlElement>() {
+ @Override
+ public boolean apply(@Nullable XmlElement xmlElement) {
+ return xmlElement.getName().equals(childName);
+ }
+ }));
+ }
+
+ public List<XmlElement> getChildElementsWithinNamespace(final String namespace) {
+ return getChildElementsInternal(new ElementFilteringStrategy() {
+ @Override
+ public boolean accept(Element e) {
+ return XmlElement.fromDomElement(e).getNamespace().equals(namespace);
+ }
+
+ });
+ }
+
+ public List<XmlElement> getChildElements(final String tagName) {
+ return getChildElementsInternal(new ElementFilteringStrategy() {
+ @Override
+ public boolean accept(Element e) {
+ return e.getTagName().equals(tagName);
+ }
+ });
+ }
+
+ public XmlElement getOnlyChildElement(String childName) {
+ List<XmlElement> nameElements = getChildElements(childName);
+ Preconditions.checkState(nameElements.size() == 1, "One element " + childName + " expected in " + toString());
+ return nameElements.get(0);
+ }
+
+ public Optional<XmlElement> getOnlyChildElementOptionally(String childName) {
+ try {
+ return Optional.of(getOnlyChildElement(childName));
+ } catch (Exception e) {
+ return Optional.absent();
+ }
+ }
+
+ public Optional<XmlElement> getOnlyChildElementOptionally(String childName, String namespace) {
+ try {
+ return Optional.of(getOnlyChildElement(childName, namespace));
+ } catch (Exception e) {
+ return Optional.absent();
+ }
+ }
+
+ public XmlElement getOnlyChildElementWithSameNamespace(String childName) {
+ return getOnlyChildElement(childName, getNamespace());
+ }
+
+ public Optional<XmlElement> getOnlyChildElementWithSameNamespaceOptionally(String childName) {
+ try {
+ return Optional.of(getOnlyChildElement(childName, getNamespace()));
+ } catch (Exception e) {
+ return Optional.absent();
+ }
+ }
+
+ public XmlElement getOnlyChildElementWithSameNamespace() {
+ XmlElement childElement = getOnlyChildElement();
+ childElement.checkNamespace(getNamespace());
+ return childElement;
+ }
+
+ public Optional<XmlElement> getOnlyChildElementWithSameNamespaceOptionally() {
+ try {
+ XmlElement childElement = getOnlyChildElement();
+ childElement.checkNamespace(getNamespace());
+ return Optional.of(childElement);
+ } catch (Exception e) {
+ return Optional.absent();
+ }
+ }
+
+ public XmlElement getOnlyChildElement(final String childName, String namespace) {
+ List<XmlElement> children = getChildElementsWithinNamespace(namespace);
+ children = Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
+ @Override
+ public boolean apply(@Nullable XmlElement xmlElement) {
+ return xmlElement.getName().equals(childName);
+ }
+ }));
+ Preconditions.checkState(children.size() == 1, "One element %s:%s expected in %s but was %s", namespace,
+ childName, toString(), children.size());
+ return children.get(0);
+ }
+
+ public XmlElement getOnlyChildElement() {
+ List<XmlElement> children = getChildElements();
+ Preconditions.checkState(children.size() == 1, "One element expected in %s but was %s", toString(),
+ children.size());
+ return children.get(0);
+ }
+
+ public String getTextContent() {
+ Node textChild = element.getFirstChild();
+ Preconditions.checkState(textChild instanceof Text, getName() + " should contain text");
+ String content = textChild.getTextContent();
+ // Trim needed
+ return content.trim();
+ }
+
+ public String getNamespaceAttribute() {
+ String attribute = element.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY);
+ Preconditions.checkState(attribute != null && !attribute.equals(""), "Element %s must specify a %s attribute",
+ toString(), XmlUtil.XMLNS_ATTRIBUTE_KEY);
+ return attribute;
+ }
+
+ public String getNamespace() {
+ String namespaceURI = element.getNamespaceURI();
+ Preconditions.checkState(namespaceURI != null, "No namespace defined for %s", this);
+ return namespaceURI.toString();
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("XmlElement{");
+ sb.append("name='").append(getName()).append('\'');
+ if (element.getNamespaceURI() != null) {
+ sb.append(", namespace='").append(getNamespace()).append('\'');
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /**
+ * Search for element's attributes defining namespaces. Look for the one
+ * namespace that matches prefix of element's text content. E.g.
+ *
+ * <pre>
+ * <type
+ * xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">th-java:threadfactory-naming</type>
+ * </pre>
+ *
+ * returns {"th-java","urn:.."}. If no prefix is matched, then default
+ * namespace is returned with empty string as key. If no default namespace
+ * is found value will be null.
+ */
+ public Map.Entry<String/* prefix */, String/* namespace */> findNamespaceOfTextContent() {
+ Map<String, String> namespaces = extractNamespaces(element);
+ String textContent = getTextContent();
+ int indexOfColon = textContent.indexOf(":");
+ String prefix;
+ if (indexOfColon > -1) {
+ prefix = textContent.substring(0, indexOfColon);
+ } else {
+ prefix = "";
+ }
+ if (namespaces.containsKey(prefix) == false) {
+ throw new IllegalArgumentException("Cannot find namespace for " + element + ". Prefix from content is "
+ + prefix + ". Found namespaces " + namespaces);
+ }
+ return Maps.immutableEntry(prefix, namespaces.get(prefix));
+ }
+
+ public List<XmlElement> getChildElementsWithSameNamespace(final String childName) {
+ List<XmlElement> children = getChildElementsWithinNamespace(getNamespace());
+ return Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
+ @Override
+ public boolean apply(@Nullable XmlElement xmlElement) {
+ return xmlElement.getName().equals(childName);
+ }
+ }));
+ }
+
+ public void checkUnrecognisedElements(List<XmlElement> recognisedElements,
+ XmlElement... additionalRecognisedElements) {
+ List<XmlElement> childElements = getChildElements();
+ childElements.removeAll(recognisedElements);
+ for (XmlElement additionalRecognisedElement : additionalRecognisedElements) {
+ childElements.remove(additionalRecognisedElement);
+ }
+ Preconditions.checkState(childElements.isEmpty(), "Unrecognised elements %s in %s", childElements, this);
+ }
+
+ public void checkUnrecognisedElements(XmlElement... additionalRecognisedElements) {
+ checkUnrecognisedElements(Collections.<XmlElement> emptyList(), additionalRecognisedElements);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ XmlElement that = (XmlElement) o;
+
+ if (!element.isEqualNode(that.element))
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return element.hashCode();
+ }
+
+ private static interface ElementFilteringStrategy {
+ boolean accept(Element e);
+ }
+}
--- /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.util.xml;
+
+public class XmlNetconfConstants {
+
+ public static final String MOUNTPOINTS = "mountpoints";
+ public static final String MOUNTPOINT = "mountpoint";
+ public static final String ID = "id";
+ public static final String CAPABILITY = "capability";
+ public static final String CAPABILITIES = "capabilities";
+ public static final String COMMIT = "commit";
+ public static final String TYPE_KEY = "type";
+ public static final String MODULE_KEY = "module";
+ public static final String INSTANCE_KEY = "instance";
+ public static final String OPERATION_ATTR_KEY = "operation";
+ public static final String SERVICES_KEY = "services";
+ public static final String CONFIG_KEY = "config";
+ public static final String MODULES_KEY = "modules";
+ public static final String CONFIGURATION_KEY = "configuration";
+ public static final String DATA_KEY = "data";
+ public static final String OK = "ok";
+ public static final String FILTER = "filter";
+ public static final String SOURCE_KEY = "source";
+ public static final String RPC_KEY = "rpc";
+ public static final String RPC_REPLY_KEY = "rpc-reply";
+ public static final String RPC_ERROR = "rpc-error";
+ public static final String NAME_KEY = "name";
+ //
+ //
+ public static final String RFC4741_TARGET_NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0";
+ public static final String URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0 = "urn:ietf:params:xml:ns:netconf:base:1.0";
+ public static final String URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+ // TODO where to store namespace of config ?
+ public static final String URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG = "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.netconf.util.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.Validator;
+
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import com.google.common.base.Preconditions;
+
+public class XmlNetconfValidator {
+ static final Schema schema;
+
+ static {
+ final InputStream xmlSchema = XmlNetconfValidator.class.getResourceAsStream("/xml.xsd");
+ Preconditions.checkNotNull(xmlSchema, "Cannot find xml.xsd");
+
+ final InputStream rfc4714Schema = XmlNetconfValidator.class.getResourceAsStream("/rfc4741.xsd");
+ Preconditions.checkNotNull(rfc4714Schema, "Cannot find rfc4741.xsd");
+ schema = XmlUtil.loadSchema(xmlSchema, rfc4714Schema);
+ }
+
+ public static void validate(Document inputDocument) throws SAXException, IOException {
+ final Validator validator = schema.newValidator();
+ final Source source = new DOMSource(inputDocument);
+ validator.validate(source);
+ }
+}
--- /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.util.xml;
+
+import java.io.*;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+import com.google.common.base.Charsets;
+
+public class XmlUtil {
+
+ public static final String XMLNS_ATTRIBUTE_KEY = "xmlns";
+
+ public static Element readXmlToElement(String xmlContent) throws SAXException, IOException {
+ Document doc = readXmlToDocument(xmlContent);
+ return doc.getDocumentElement();
+ }
+
+ public static Element readXmlToElement(InputStream xmlContent) throws SAXException, IOException {
+ Document doc = readXmlToDocument(xmlContent);
+ return doc.getDocumentElement();
+ }
+
+ public static Document readXmlToDocument(String xmlContent) throws SAXException, IOException {
+ return readXmlToDocument(new ByteArrayInputStream(xmlContent.getBytes(Charsets.UTF_8)));
+ }
+
+ public static Document readXmlToDocument(InputStream xmlContent) throws SAXException, IOException {
+ DocumentBuilderFactory factory = getDocumentBuilderFactory();
+ DocumentBuilder dBuilder;
+ try {
+ dBuilder = factory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ Document doc = dBuilder.parse(xmlContent);
+
+ doc.getDocumentElement().normalize();
+ return doc;
+ }
+
+ public static Element readXmlToElement(File xmlFile) throws SAXException, IOException {
+ return readXmlToDocument(new FileInputStream(xmlFile)).getDocumentElement();
+ }
+
+ private static final DocumentBuilderFactory getDocumentBuilderFactory() {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setCoalescing(true);
+ // factory.setValidating(true);
+ factory.setIgnoringElementContentWhitespace(true);
+ factory.setIgnoringComments(true);
+ return factory;
+ }
+
+ public static Document newDocument() {
+ DocumentBuilderFactory factory = getDocumentBuilderFactory();
+ try {
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.newDocument();
+ return document;
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Element createTextElement(Document document, String name, String content) {
+ Element typeElement = document.createElement(name);
+ typeElement.appendChild(document.createTextNode(content));
+ return typeElement;
+ }
+
+ public static void addNamespaceAttr(Element root, String namespace) {
+ root.setAttribute(XMLNS_ATTRIBUTE_KEY, namespace);
+ }
+
+ public static void addPrefixedNamespaceAttr(Element root, String prefix, String namespace) {
+ root.setAttribute(concat(XMLNS_ATTRIBUTE_KEY, prefix), namespace);
+ }
+
+ public static Element createPrefixedTextElement(Document document, String key, String prefix, String moduleName) {
+ return createTextElement(document, key, concat(prefix, moduleName));
+ }
+
+ private static String concat(String prefix, String value) {
+ return prefix + ":" + value;
+ }
+
+ public static String toString(Document document) {
+ return toString(document.getDocumentElement());
+ }
+
+ public static String toString(Element xml) {
+ return toString(xml, false);
+ }
+
+ public static String toString(XmlElement xmlElement) {
+ return toString(xmlElement.getDomElement(), false);
+ }
+
+ public static String toString(Element xml, boolean addXmlDeclaration) {
+ try {
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, addXmlDeclaration == true ? "no" : "yes");
+
+ StreamResult result = new StreamResult(new StringWriter());
+ DOMSource source = new DOMSource(xml);
+ transformer.transform(source, result);
+
+ String xmlString = result.getWriter().toString();
+ return xmlString;
+ } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+ throw new RuntimeException("Unable to serialize xml element " + xml, e);
+ }
+ }
+
+ public static String toString(Document doc, boolean addXmlDeclaration) {
+ return toString(doc.getDocumentElement(), addXmlDeclaration);
+ }
+
+ public static Schema loadSchema(InputStream... fromStreams) {
+ Source[] sources = new Source[fromStreams.length];
+ int i = 0;
+ for (InputStream stream : fromStreams) {
+ sources[i++] = new StreamSource(stream);
+ }
+
+ final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ try {
+ return schemaFactory.newSchema(sources);
+ } catch (SAXException e) {
+ throw new IllegalStateException("Failed to instantiate XML schema", e);
+ }
+ }
+
+ public static Object evaluateXPath(XPathExpression expr, Object rootNode, QName returnType) {
+ try {
+ return expr.evaluate(rootNode, returnType);
+ } catch (XPathExpressionException e) {
+ throw new IllegalStateException("Error while evaluating xpath expression " + expr, e);
+ }
+ }
+
+ public static Document createDocumentCopy(Document original) {
+ final Document copiedDocument = newDocument();
+ final Node copiedRoot = copiedDocument.importNode(original.getDocumentElement(), true);
+ copiedDocument.appendChild(copiedRoot);
+ return copiedDocument;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <rpc-error>
+ <error-type>*** transport/rpc/protocol/application</error-type>
+ <error-tag>*** RFC 4741 Appendix A. NETCONF Error List</error-tag>
+ <error-severity>*** error/warning</error-severity>
+
+ <!--
+ <error-info>
+ <bad-attribute>message-id</bad-attribute>
+ <bad-element>rpc</bad-element>
+ </error-info>
+ -->
+
+ </rpc-error>
+</rpc-reply>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
+ targetNamespace="urn:ietf:params:xml:ns:netconf:base:1.0"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xml:lang="en">
+ <!--
+ import standard XML definitions
+ -->
+ <xs:import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2001/xml.xsd">
+
+ <xs:annotation>
+ <xs:documentation>
+ This import accesses the xml: attribute groups for the
+ xml:lang as declared on the error-message element.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:import>
+ <!--
+ message-id attribute
+ -->
+ <xs:simpleType name="messageIdType">
+ <xs:restriction base="xs:string">
+ <xs:maxLength value="4095"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ Types used for session-id
+ -->
+ <xs:simpleType name="SessionId">
+ <xs:restriction base="xs:unsignedInt">
+ <xs:minInclusive value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="SessionIdOrZero">
+ <xs:restriction base="xs:unsignedInt"/>
+ </xs:simpleType>
+ <!--
+ <rpc> element
+ -->
+ <xs:complexType name="rpcType">
+ <xs:sequence>
+ <xs:element ref="rpcOperation"/>
+
+
+ </xs:sequence>
+ <xs:attribute name="message-id" type="messageIdType"
+ use="required"/>
+ <!--
+ Arbitrary attributes can be supplied with <rpc> element.
+ -->
+ <xs:anyAttribute processContents="lax"/>
+ </xs:complexType>
+ <xs:element name="rpc" type="rpcType"/>
+ <!--
+ data types and elements used to construct rpc-errors
+ -->
+ <xs:simpleType name="ErrorType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="transport"/>
+ <xs:enumeration value="rpc"/>
+ <xs:enumeration value="protocol"/>
+ <xs:enumeration value="application"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="ErrorTag">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="in-use"/>
+ <xs:enumeration value="invalid-value"/>
+ <xs:enumeration value="too-big"/>
+ <xs:enumeration value="missing-attribute"/>
+ <xs:enumeration value="bad-attribute"/>
+ <xs:enumeration value="unknown-attribute"/>
+ <xs:enumeration value="missing-element"/>
+ <xs:enumeration value="bad-element"/>
+ <xs:enumeration value="unknown-element"/>
+ <xs:enumeration value="unknown-namespace"/>
+ <xs:enumeration value="access-denied"/>
+ <xs:enumeration value="lock-denied"/>
+ <xs:enumeration value="resource-denied"/>
+ <xs:enumeration value="rollback-failed"/>
+ <xs:enumeration value="data-exists"/>
+ <xs:enumeration value="data-missing"/>
+ <xs:enumeration value="operation-not-supported"/>
+ <xs:enumeration value="operation-failed"/>
+ <xs:enumeration value="partial-operation"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="ErrorSeverity">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="error"/>
+ <xs:enumeration value="warning"/>
+ </xs:restriction>
+
+
+ </xs:simpleType>
+ <xs:complexType name="errorInfoType">
+ <xs:sequence>
+ <xs:choice>
+ <xs:element name="session-id" type="SessionIdOrZero"/>
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:sequence>
+ <xs:element name="bad-attribute" type="xs:QName"
+ minOccurs="0" maxOccurs="1"/>
+ <xs:element name="bad-element" type="xs:QName"
+ minOccurs="0" maxOccurs="1"/>
+ <xs:element name="ok-element" type="xs:QName"
+ minOccurs="0" maxOccurs="1"/>
+ <xs:element name="err-element" type="xs:QName"
+ minOccurs="0" maxOccurs="1"/>
+ <xs:element name="noop-element" type="xs:QName"
+ minOccurs="0" maxOccurs="1"/>
+ <xs:element name="bad-namespace" type="xs:QName"
+ minOccurs="0" maxOccurs="1"/>
+ </xs:sequence>
+ </xs:sequence>
+ </xs:choice>
+ <!-- elements from any other namespace are also allowed
+ to follow the NETCONF elements -->
+ <xs:any namespace="##other"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="rpcErrorType">
+ <xs:sequence>
+ <xs:element name="error-type" type="ErrorType"/>
+ <xs:element name="error-tag" type="ErrorTag"/>
+ <xs:element name="error-severity" type="ErrorSeverity"/>
+ <xs:element name="error-app-tag" type="xs:string"
+ minOccurs="0"/>
+ <xs:element name="error-path" type="xs:string" minOccurs="0"/>
+ <xs:element name="error-message" minOccurs="0">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute ref="xml:lang" use="optional"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="error-info" type="errorInfoType"
+ minOccurs="0"/>
+ </xs:sequence>
+
+
+ </xs:complexType>
+ <!--
+ <rpc-reply> element
+ -->
+ <xs:complexType name="rpcReplyType">
+ <xs:choice>
+ <xs:element name="ok"/>
+ <xs:group ref="rpcResponse"/>
+ </xs:choice>
+ <xs:attribute name="message-id" type="messageIdType"
+ use="optional"/>
+ <!--
+ Any attributes supplied with <rpc> element must be returned
+ on <rpc-reply>.
+ -->
+ <xs:anyAttribute processContents="lax"/>
+ </xs:complexType>
+ <xs:group name="rpcResponse">
+ <xs:sequence>
+ <xs:element name="rpc-error" type="rpcErrorType"
+ minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="data" type="dataInlineType" minOccurs="0"/>
+ </xs:sequence>
+ </xs:group>
+ <xs:element name="rpc-reply" type="rpcReplyType"/>
+ <!--
+ Type for <test-option> parameter to <edit-config>
+ -->
+ <xs:simpleType name="testOptionType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="test-then-set"/>
+ <xs:enumeration value="set"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ Type for <error-option> parameter to <edit-config>
+ -->
+ <xs:simpleType name="errorOptionType">
+ <xs:restriction base="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ Use of the rollback-on-error value requires
+ the :rollback-on-error capability.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:enumeration value="stop-on-error"/>
+ <xs:enumeration value="continue-on-error"/>
+ <xs:enumeration value="rollback-on-error"/>
+
+
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ rpcOperationType: used as a base type for all
+ NETCONF operations
+ -->
+ <xs:complexType name="rpcOperationType"/>
+ <xs:element name="rpcOperation"
+ type="rpcOperationType" abstract="true"/>
+ <!--
+ Type for <config> element
+ -->
+ <xs:complexType name="configInlineType">
+ <xs:complexContent>
+ <xs:extension base="xs:anyType"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <!--
+ Type for <data> element
+ -->
+ <xs:complexType name="dataInlineType">
+ <xs:complexContent>
+ <xs:extension base="xs:anyType"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <!--
+ Type for <filter> element
+ -->
+ <xs:simpleType name="FilterType">
+ <xs:restriction base="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ Use of the xpath value requires the :xpath capability.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:enumeration value="subtree"/>
+ <xs:enumeration value="xpath"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="filterInlineType">
+ <xs:complexContent>
+ <xs:extension base="xs:anyType">
+ <xs:attribute name="type"
+ type="FilterType" default="subtree"/>
+ <!-- if type="xpath", the xpath expression
+ appears in the select element -->
+ <xs:attribute name="select"/>
+ </xs:extension>
+
+
+ </xs:complexContent>
+ </xs:complexType>
+ <!--
+ configuration datastore names
+ -->
+ <xs:annotation>
+ <xs:documentation>
+ The startup datastore can be used only if the :startup
+ capability is advertised. The candidate datastore can
+ be used only if the :candidate datastore is advertised.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType name="configNameType"/>
+ <xs:element name="config-name"
+ type="configNameType" abstract="true"/>
+ <xs:element name="startup" type="configNameType"
+ substitutionGroup="config-name"/>
+ <xs:element name="candidate" type="configNameType"
+ substitutionGroup="config-name"/>
+ <xs:element name="running" type="configNameType"
+ substitutionGroup="config-name"/>
+ <!--
+ operation attribute used in <edit-config>
+ -->
+ <xs:simpleType name="editOperationType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="merge"/>
+ <xs:enumeration value="replace"/>
+ <xs:enumeration value="create"/>
+ <xs:enumeration value="delete"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:attribute name="operation"
+ type="editOperationType" default="merge"/>
+ <!--
+ <default-operation> element
+ -->
+ <xs:simpleType name="defaultOperationType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="merge"/>
+ <xs:enumeration value="replace"/>
+ <xs:enumeration value="none"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ <url> element
+ -->
+ <xs:complexType name="configURIType">
+
+
+ <xs:annotation>
+ <xs:documentation>
+ Use of the url element requires the :url capability.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="xs:anyURI"/>
+ </xs:simpleContent>
+ </xs:complexType>
+ <!--
+ Type for <source> element (except <get-config>)
+ -->
+ <xs:complexType name="rpcOperationSourceType">
+ <xs:choice>
+ <xs:element name="config" type="configInlineType"/>
+ <xs:element ref="config-name"/>
+ <xs:element name="url" type="configURIType"/>
+ </xs:choice>
+ </xs:complexType>
+ <!--
+ Type for <source> element in <get-config>
+ -->
+ <xs:complexType name="getConfigSourceType">
+ <xs:choice>
+ <xs:element ref="config-name"/>
+ <xs:element name="url" type="configURIType"/>
+ </xs:choice>
+ </xs:complexType>
+ <!--
+ Type for <target> element
+ -->
+ <xs:complexType name="rpcOperationTargetType">
+ <xs:choice>
+ <xs:element ref="config-name"/>
+ <xs:element name="url" type="configURIType"/>
+ </xs:choice>
+ </xs:complexType>
+ <!--
+ <get-config> operation
+ -->
+ <xs:complexType name="getConfigType">
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:element name="source"
+ type="getConfigSourceType"/>
+ <xs:element name="filter"
+ type="filterInlineType" minOccurs="0"/>
+
+
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="get-config" type="getConfigType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <edit-config> operation
+ -->
+ <xs:complexType name="editConfigType">
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:annotation>
+ <xs:documentation>
+ Use of the test-option element requires the
+ :validate capability. Use of the url element
+ requires the :url capability.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:element name="target"
+ type="rpcOperationTargetType"/>
+ <xs:element name="default-operation"
+ type="defaultOperationType"
+ minOccurs="0"/>
+ <xs:element name="test-option"
+ type="testOptionType"
+ minOccurs="0"/>
+ <xs:element name="error-option"
+ type="errorOptionType"
+ minOccurs="0"/>
+ <xs:choice>
+ <xs:element name="config"
+ type="configInlineType"/>
+ <xs:element name="url"
+ type="configURIType"/>
+ </xs:choice>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="edit-config" type="editConfigType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <copy-config> operation
+ -->
+ <xs:complexType name="copyConfigType">
+ <xs:complexContent>
+
+
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:element name="target" type="rpcOperationTargetType"/>
+ <xs:element name="source" type="rpcOperationSourceType"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="copy-config" type="copyConfigType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <delete-config> operation
+ -->
+ <xs:complexType name="deleteConfigType">
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:element name="target" type="rpcOperationTargetType"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="delete-config" type="deleteConfigType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <get> operation
+ -->
+ <xs:complexType name="getType">
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:element name="filter"
+ type="filterInlineType" minOccurs="0"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="get" type="getType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <lock> operation
+ -->
+ <xs:complexType name="lockType">
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:element name="target"
+ type="rpcOperationTargetType"/>
+
+
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="lock" type="lockType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <unlock> operation
+ -->
+ <xs:complexType name="unlockType">
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:element name="target" type="rpcOperationTargetType"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="unlock" type="unlockType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <operations> operation
+ -->
+ <xs:complexType name="validateType">
+ <xs:annotation>
+ <xs:documentation>
+ The validate operation requires the :validate capability.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:element name="source" type="rpcOperationSourceType"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="validate" type="validateType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <commit> operation
+ -->
+ <xs:simpleType name="confirmTimeoutType">
+ <xs:restriction base="xs:unsignedInt">
+ <xs:minInclusive value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="commitType">
+
+
+ <xs:annotation>
+ <xs:documentation>
+ The commit operation requires the :candidate capability.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:annotation>
+ <xs:documentation>
+ Use of the confirmed and confirm-timeout elements
+ requires the :confirmed-commit capability.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:element name="confirmed" minOccurs="0"/>
+ <xs:element name="confirm-timeout"
+ type="confirmTimeoutType"
+ minOccurs="0"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="commit" type="commitType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <discard-changes> operation
+ -->
+ <xs:complexType name="discardChangesType">
+ <xs:annotation>
+ <xs:documentation>
+ The discard-changes operation requires the
+ :candidate capability.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="discard-changes"
+ type="discardChangesType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <close-session> operation
+ -->
+ <xs:complexType name="closeSessionType">
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType"/>
+ </xs:complexContent>
+
+
+ </xs:complexType>
+ <xs:element name="close-session" type="closeSessionType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <kill-session> operation
+ -->
+ <xs:complexType name="killSessionType">
+ <xs:complexContent>
+ <xs:extension base="rpcOperationType">
+ <xs:sequence>
+ <xs:element name="session-id"
+ type="SessionId" minOccurs="1"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="kill-session" type="killSessionType"
+ substitutionGroup="rpcOperation"/>
+ <!--
+ <hello> element
+ -->
+ <xs:element name="hello">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="capabilities">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="capability" type="xs:anyURI"
+ maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="session-id"
+ type="SessionId" minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
--- /dev/null
+<?xml version='1.0'?>
+<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xml:lang="en">
+
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h1>About the XML namespace</h1>
+
+ <div class="bodytext">
+ <p>
+ This schema document describes the XML namespace, in a form
+ suitable for import by other schema documents.
+ </p>
+ <p>
+ See
+ <a href="http://www.w3.org/XML/1998/namespace.html">
+ http://www.w3.org/XML/1998/namespace.html
+ </a>
+ and
+ <a href="http://www.w3.org/TR/REC-xml">
+ http://www.w3.org/TR/REC-xml
+ </a>
+ for information
+ about this namespace.
+ </p>
+ <p>
+ Note that local names in this namespace are intended to be
+ defined only by the World Wide Web Consortium or its subgroups.
+ The names currently defined in this namespace are listed below.
+ They should not be used with conflicting semantics by any Working
+ Group, specification, or document instance.
+ </p>
+ <p>
+ See further below in this document for more information about
+ <a
+ href="#usage">how to refer to this schema document from your own
+ XSD schema documents
+ </a>
+ and about<a href="#nsversioning">the
+ namespace-versioning policy governing this schema document</a>.
+ </p>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>lang (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ is a language code for the natural language of the content of
+ any element; its value is inherited. This name is reserved
+ by virtue of its definition in the XML specification.
+ </p>
+
+ </div>
+ <div>
+ <h4>Notes</h4>
+ <p>
+ Attempting to install the relevant ISO 2- and 3-letter
+ codes as the enumerated possible values is probably never
+ going to be a realistic possibility.
+ </p>
+ <p>
+ See BCP 47 at
+ <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
+ http://www.rfc-editor.org/rfc/bcp/bcp47.txt
+ </a>
+ and the IANA language subtag registry at
+ <a href="http://www.iana.org/assignments/language-subtag-registry">
+ http://www.iana.org/assignments/language-subtag-registry
+ </a>
+ for further information.
+ </p>
+ <p>
+ The union allows for the 'un-declaration' of xml:lang with
+ the empty string.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:union memberTypes="xs:language">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value=""/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>space (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose
+ value is a keyword indicating what whitespace processing
+ discipline is intended for the content of the element; its
+ value is inherited. This name is reserved by virtue of its
+ definition in the XML specification.
+ </p>
+
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:NCName">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="base" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>base (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ provides a URI to be used as the base for interpreting any
+ relative URIs in the scope of the element on which it
+ appears; its value is inherited. This name is reserved
+ by virtue of its definition in the XML Base specification.
+ </p>
+
+ <p>
+ See
+ <a
+ href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/
+ </a>
+ for information about this attribute.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="id" type="xs:ID">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>id (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ should be interpreted as if declared to be of type ID.
+ This name is reserved by virtue of its definition in the
+ xml:id specification.
+ </p>
+
+ <p>
+ See
+ <a
+ href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/
+ </a>
+ for information about this attribute.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+ <xs:attribute ref="xml:base"/>
+ <xs:attribute ref="xml:lang"/>
+ <xs:attribute ref="xml:space"/>
+ <xs:attribute ref="xml:id"/>
+ </xs:attributeGroup>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>Father (in any context at all)</h3>
+
+ <div class="bodytext">
+ <p>
+ denotes Jon Bosak, the chair of
+ the original XML Working Group. This name is reserved by
+ the following decision of the W3C XML Plenary and
+ XML Coordination groups:
+ </p>
+ <blockquote>
+ <p>
+ In appreciation for his vision, leadership and
+ dedication the W3C XML Plenary on this 10th day of
+ February, 2000, reserves for Jon Bosak in perpetuity
+ the XML name "xml:Father".
+ </p>
+ </blockquote>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div xml:id="usage" id="usage">
+ <h2>
+ <a name="usage">About this schema document</a>
+ </h2>
+
+ <div class="bodytext">
+ <p>
+ This schema defines attributes and an attribute group suitable
+ for use by schemas wishing to allow<code>xml:base</code>,
+ <code>xml:lang</code>,
+ <code>xml:space</code>
+ or
+ <code>xml:id</code>
+ attributes on elements they define.
+ </p>
+ <p>
+ To enable this, such a schema must import this schema for
+ the XML namespace, e.g. as follows:
+ </p>
+ <pre>
+ <schema . . .>
+ . . .
+ <import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+ </pre>
+ <p>
+ or
+ </p>
+ <pre>
+ <import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+ </pre>
+ <p>
+ Subsequently, qualified reference to any of the attributes or the
+ group defined below will have the desired effect, e.g.
+ </p>
+ <pre>
+ <type . . .>
+ . . .
+ <attributeGroup ref="xml:specialAttrs"/>
+ </pre>
+ <p>
+ will define a type which will schema-validate an instance element
+ with any of those attributes.
+ </p>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div id="nsversioning" xml:id="nsversioning">
+ <h2>
+ <a name="nsversioning">Versioning policy for this schema document</a>
+ </h2>
+ <div class="bodytext">
+ <p>
+ In keeping with the XML Schema WG's standard versioning
+ policy, this schema document will persist at
+ <a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd</a>.
+ </p>
+ <p>
+ At the date of issue it can also be found at
+ <a href="http://www.w3.org/2001/xml.xsd">
+ http://www.w3.org/2001/xml.xsd</a>.
+ </p>
+ <p>
+ The schema document at that URI may however change in the future,
+ in order to remain compatible with the latest version of XML
+ Schema itself, or with the XML namespace itself. In other words,
+ if the XML Schema or XML namespaces change, the version of this
+ document at
+ <a href="http://www.w3.org/2001/xml.xsd">
+ http://www.w3.org/2001/xml.xsd
+ </a>
+ will change accordingly; the version at
+ <a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd
+ </a>
+ will not change.
+ </p>
+ <p>
+ Previous dated (and unchanging) versions of this schema
+ document are at:
+ </p>
+ <ul>
+ <li>
+ <a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd
+ </a>
+ </li>
+ <li>
+ <a href="http://www.w3.org/2007/08/xml.xsd">
+ http://www.w3.org/2007/08/xml.xsd
+ </a>
+ </li>
+ <li>
+ <a href="http://www.w3.org/2004/10/xml.xsd">
+ http://www.w3.org/2004/10/xml.xsd
+ </a>
+ </li>
+ <li>
+ <a href="http://www.w3.org/2001/03/xml.xsd">
+ http://www.w3.org/2001/03/xml.xsd
+ </a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+</xs:schema>
+
--- /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.util.test;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+import com.google.common.io.CharStreams;
+import com.google.common.io.InputSupplier;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class XmlFileLoader {
+
+ public static NetconfMessage xmlFileToNetconfMessage(final String fileName) throws IOException, SAXException,
+ ParserConfigurationException {
+ return new NetconfMessage(xmlFileToDocument(fileName));
+ }
+
+ public static Element xmlFileToElement(final String fileName) throws IOException, SAXException,
+ ParserConfigurationException {
+ return xmlFileToDocument(fileName).getDocumentElement();
+ }
+
+ public static String xmlFileToString(final String fileName) throws IOException, SAXException,
+ ParserConfigurationException {
+ return XmlUtil.toString(xmlFileToDocument(fileName));
+ }
+
+ public static Document xmlFileToDocument(final String fileName) throws IOException, SAXException,
+ ParserConfigurationException {
+ try (InputStream resourceAsStream = XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName)) {
+ Preconditions.checkNotNull(resourceAsStream);
+ final Document doc = XmlUtil.readXmlToDocument(resourceAsStream);
+ return doc;
+ }
+ }
+
+ public static String fileToString(final String fileName) throws IOException {
+ try (InputStream resourceAsStream = XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName)) {
+ Preconditions.checkNotNull(resourceAsStream);
+
+ InputSupplier<? extends InputStream> supplier = new InputSupplier<InputStream>() {
+ @Override
+ public InputStream getInput() throws IOException {
+ return resourceAsStream;
+ }
+ };
+
+ InputSupplier<InputStreamReader> readerSupplier = CharStreams.newReaderSupplier(supplier, Charsets.UTF_8);
+
+ return CharStreams.toString(readerSupplier);
+ }
+ }
+
+ public static InputStream getResourceAsStream(final String fileName) {
+ return XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName);
+ }
+}
--- /dev/null
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <capabilities>
+ <capability>urn:ietf:params:netconf:base:1.0</capability>
+ </capabilities>
+</hello>
--- /dev/null
+[tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;]
+ <?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.0</capability>
+ </capabilities>
+</hello>
--- /dev/null
+<rpc message-id="103" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <close-session/>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="1" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <close-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+ <commit></commit>
+</rpc>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <rpc-error>
+ <error-type>protocol</error-type>
+ <error-tag>unknown-element</error-tag>
+ <error-severity>error</error-severity>
+ <error-info>
+ <bad-element>rpc-reply</bad-element>
+ </error-info>
+ </rpc-error>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="*** replaced by message id ***"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <ok/>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <rpc-error>
+ <error-type>rpc</error-type>
+ <error-tag>missing-attribute</error-tag>
+ <error-severity>error</error-severity>
+ <error-info>
+ <bad-attribute>message-id</bad-attribute>
+ <bad-element>rpc</bad-element>
+ </error-info>
+ </rpc-error>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get/>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get-config>
+ <source>
+ <running/>
+ </source>
+ <filter type="xpath" select="/top/jmxbean[objectName='org.opendaylight.controller:type=ConfigRegistry']"/>
+ </get-config>
+</rpc>
--- /dev/null
+<rpc-reply message-id="101"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <jmxbean
+ objectName="org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean">
+ <ExportedInterfaces>
+ <Entries>
+ <Entry>testing-threadpool</Entry>
+ <Entry>modifiable-threadpool</Entry>
+ </Entries>
+ </ExportedInterfaces>
+ <ImplementationName>fixed</ImplementationName>
+ <ThreadCount>10</ThreadCount>
+ <TriggerNewInstanceCreation>false</TriggerNewInstanceCreation>
+ </jmxbean>
+ <jmxbean
+ objectName="org.opendaylight.controller:instanceName=fixed1,interfaceName=modifiable-threadpool,type=ConfigBean">
+ <ExportedInterfaces>
+ <Entries>
+ <Entry>testing-threadpool</Entry>
+ <Entry>modifiable-threadpool</Entry>
+ </Entries>
+ </ExportedInterfaces>
+ <ImplementationName>fixed</ImplementationName>
+ <ThreadCount>10</ThreadCount>
+ <TriggerNewInstanceCreation>false</TriggerNewInstanceCreation>
+ </jmxbean>
+ </data>
+</rpc-reply>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <discard-changes></discard-changes>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+
+ <name>test1</name>
+
+ <simple-long-2>44</simple-long-2>
+ <binaryLeaf>8</binaryLeaf>
+ <binaryLeaf>1</binaryLeaf>
+ <binaryLeaf>0</binaryLeaf>
+ <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+ <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ <simple-int1>444</simple-int1>
+ <simple-int2>4444</simple-int2>
+ <simple-int3>454</simple-int3>
+ <complex-dto-bInner>
+ <simple-int3>44</simple-int3>
+ <deep>
+ <simple-int3>4</simple-int3>
+ </deep>
+ <simple-list>4</simple-list>
+ </complex-dto-bInner>
+ <simple-list>4</simple-list>
+ </dto_d>
+ <simpleInt>44</simpleInt>
+ <simple-test>545</simple-test>
+ <simple-long>454545</simple-long>
+ <simpleBoolean>false</simpleBoolean>
+ <dto-c>
+ <dto-a-inner>
+ <dto-a-inner-inner>
+ <simple-arg>456</simple-arg>
+ </dto-a-inner-inner>
+ <simple-arg>44</simple-arg>
+ </dto-a-inner>
+ </dto-c>
+ <simple-short>4</simple-short>
+ <simple-BigInteger>999</simple-BigInteger>
+ <simple-byte>4</simple-byte>
+ <peers>
+ <port>port1</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <peers>
+ <port>port23</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test2</name>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_dep_2</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_test1</name>
+ <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" a="64" id="a" message-id="101" xmlnx="a:b:c:d">
+ <data>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <!--<module>
+ <type>impl</type>
+ </module>
+ -->
+ <!--<module>
+ <type>impl-dep</type>
+ </module>
+ -->
+ <!--<module>
+ <type>impl-netconf</type>
+ </module>
+ -->
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"/>
+ </data>
+</rpc-reply>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc message-id="6"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>merge</default-operation>
+ <test-option>set</test-option>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="merge">
+ <name>threadfactory-naming-instance</name>
+ <type
+ xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ th-java:threadfactory-naming
+ </type>
+ <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ prefixDefinedInXML
+ </name-prefix>
+ </module>
+ </modules>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc message-id="6"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>merge</default-operation>
+ <test-option>set</test-option>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="merge">
+ <name>impl-dep-instance</name>
+ <type
+ xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:test:impl">th-java:impl-dep
+ </type>
+ </module>
+ </modules>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>none</default-operation>
+ <error-option>stop-on-error</error-option>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test1</name>
+ <simple-long-2>44</simple-long-2>
+ <binaryLeaf>8</binaryLeaf>
+ <binaryLeaf>7</binaryLeaf>
+ <binaryLeaf>9</binaryLeaf>
+ <dto_d>
+ <simple-int1>444</simple-int1>
+ <simple-int2>4444</simple-int2>
+ <simple-int3>454</simple-int3>
+ <complex-dto-bInner>
+ <simple-int3>44</simple-int3>
+ <deep>
+ <simple-int3>4</simple-int3>
+ </deep>
+ <simple-list>4</simple-list>
+ </complex-dto-bInner>
+ <simple-list>4</simple-list>
+ </dto_d>
+ <simpleInt>44</simpleInt>
+ <simple-test>545</simple-test>
+ <simple-long>454545</simple-long>
+ <simpleBoolean>false</simpleBoolean>
+ <dto-c>
+ <dto-a-inner>
+ <dto-a-inner-inner>
+ <simple-arg>456</simple-arg>
+ </dto-a-inner-inner>
+ <simple-arg>44</simple-arg>
+ </dto-a-inner>
+ </dto-c>
+ <simple-short>4</simple-short>
+ <simple-BigInteger>999</simple-BigInteger>
+ <simple-byte>4</simple-byte>
+ <peers>
+ <port>port1</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <peers>
+ <port>port23</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test2</name>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_dep_2</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_test1</name>
+ <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>none</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="remove">
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="remove">
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="remove">
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test1</name>
+ </module>
+
+ <module xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="remove">
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test2</name>
+ </module>
+ </modules>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>replace</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"/>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>replace</default-operation>
+ <config xmlns="top:level:namespace">
+ <modules>
+ <module xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl" operation="remove">
+ <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">impl-dep</type>
+ <name>dep</name>
+ </module>
+ <module xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl" operation="remove">
+ <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">impl-dep</type>
+ <name>dep2</name>
+ </module>
+ </modules>
+ <services/>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <config xmlns="top:level:namespace">
+ <modules>
+ <module xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl"
+ operation="replace">
+ <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">impl-dep</type>
+ <name>dep</name>
+ </module>
+ </modules>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>replace</default-operation>
+ <config xmlns="top:level:namespace">
+ <modules operation="replace">
+ <module xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl" operation="merge">
+ <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">impl-dep</type>
+ <name>dep</name>
+ </module>
+ </modules>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc message-id="1"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <target>
+ <candidate/>
+ </target>
+ <config/>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get/>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc id="a" a="64" xmlnx="a:b:c:d" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+ <get-config>
+ <source>
+ <running/>
+ </source>
+ </get-config>
+</rpc>
--- /dev/null
+<rpc id="a" a="64" xmlnx="a:b:c:d" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+ <get-config>
+ <source>
+ <candidate/>
+ </source>
+ </get-config>
+</rpc>
--- /dev/null
+<rpc message-id="2"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get-schema xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
+ <identifier>threadpool-api</identifier>
+ </get-schema>
+</rpc>
--- /dev/null
+<rpc message-id="2"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get-schema xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
+ <identifier>threadpool-api</identifier>
+ <version>2010-09-24</version>
+ <format
+ xmlns:ncm="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">ncm:yang
+ </format>
+ </get-schema>
+</rpc>
\ No newline at end of file
--- /dev/null
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <capabilities>
+ <capability>urn:ietf:params:netconf:base:1.0</capability>
+ </capabilities>
+ <session-id>666</session-id>
+</hello>
--- /dev/null
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
+ <capabilities>
+ <capability>urn:ietf:params:netconf:base:1.0</capability>
+ </capabilities>
+</hello>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc message-id="6"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <target>
+ <candidate/>
+ </target>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="merge">
+ <name>threadfactory-naming-instance</name>
+ <type
+ xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ th-java:threadfactory-naming
+ </type>
+ <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ prefixDefinedInXML
+ </name-prefix>
+ </module>
+ </modules>
+
+ <mountpoints>
+ <mountpoint>
+ <id>localhost:12002</id>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="merge">
+ <name>threadfactory-naming-instance</name>
+ <type
+ xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ th-java:threadfactory-naming
+ </type>
+ <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ prefixDefinedInXML
+ </name-prefix>
+ </module>
+ </modules>
+ </config>
+ </mountpoint>
+ <mountpoint>
+ <id>localhost:12003</id>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="replace">
+ <name>threadfactory-naming-instance</name>
+ <type
+ xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ th-java:threadfactory-naming
+ </type>
+ <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ prefixDefinedInXML
+ </name-prefix>
+ </module>
+ </modules>
+ </config>
+ </mountpoint>
+ </mountpoints>
+ </config>
+
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <mount>
+ <mountpoint-id>
+ localhost:12002
+ </mountpoint-id>
+ </mount>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <mount>
+ <mountpoint-id>
+ localhost:12003
+ </mountpoint-id>
+ </mount>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <unmount>
+ <mountpoint-id>
+ localhost:12002
+ </mountpoint-id>
+ </unmount>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+
+ <name>test1</name>
+
+ <simple-long-2>44</simple-long-2>
+ <binaryLeaf>8</binaryLeaf>
+ <binaryLeaf>1</binaryLeaf>
+ <binaryLeaf>0</binaryLeaf>
+ <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+ <dto_d>
+ <simple-int1>444</simple-int1>
+ <simple-int2 xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">4444</simple-int2>
+ <simple-int3>454</simple-int3>
+ <complex-dto-bInner>
+ <simple-int3>44</simple-int3>
+ <deep>
+ <simple-int3>4</simple-int3>
+ </deep>
+ <simple-list>4</simple-list>
+ </complex-dto-bInner>
+ <simple-list>4</simple-list>
+ </dto_d>
+ <simpleInt>44</simpleInt>
+ <simple-test>545</simple-test>
+ <simple-long>454545</simple-long>
+ <simpleBoolean>false</simpleBoolean>
+ <dto-c>
+ <dto-a-inner>
+ <dto-a-inner-inner>
+ <simple-arg>456</simple-arg>
+ </dto-a-inner-inner>
+ <simple-arg>44</simple-arg>
+ </dto-a-inner>
+ </dto-c>
+ <simple-short>4</simple-short>
+ <simple-BigInteger>999</simple-BigInteger>
+ <simple-byte>4</simple-byte>
+ <peers>
+ <port>port1</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <peers>
+ <port>port23</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test2</name>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_dep_2</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_test1</name>
+ <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+
+ <name>test1</name>
+
+ <simple-long-2>44</simple-long-2>
+ <simple-long-2 xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">44</simple-long-2>
+ <binaryLeaf>8</binaryLeaf>
+ <binaryLeaf>1</binaryLeaf>
+ <binaryLeaf>0</binaryLeaf>
+ <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+ <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ <simple-int1>444</simple-int1>
+ <simple-int2>4444</simple-int2>
+ <simple-int3>454</simple-int3>
+ <complex-dto-bInner>
+ <simple-int3>44</simple-int3>
+ <deep>
+ <simple-int3>4</simple-int3>
+ </deep>
+ <simple-list>4</simple-list>
+ </complex-dto-bInner>
+ <simple-list>4</simple-list>
+ </dto_d>
+ <simpleInt>44</simpleInt>
+ <simple-test>545</simple-test>
+ <simple-long>454545</simple-long>
+ <simpleBoolean>false</simpleBoolean>
+ <dto-c>
+ <dto-a-inner>
+ <dto-a-inner-inner>
+ <simple-arg>456</simple-arg>
+ </dto-a-inner-inner>
+ <simple-arg>44</simple-arg>
+ </dto-a-inner>
+ </dto-c>
+ <simple-short>4</simple-short>
+ <simple-BigInteger>999</simple-BigInteger>
+ <simple-byte>4</simple-byte>
+ <peers>
+ <port>port1</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <peers>
+ <port>port23</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test2</name>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_dep_2</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_test1</name>
+ <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+
+ <name>test1</name>
+
+ <simple-long-2>44</simple-long-2>
+ <binaryLeaf>8</binaryLeaf>
+ <binaryLeaf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">1</binaryLeaf>
+ <binaryLeaf>0</binaryLeaf>
+ <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+ <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ <simple-int1>444</simple-int1>
+ <simple-int2>4444</simple-int2>
+ <simple-int3>454</simple-int3>
+ <complex-dto-bInner>
+ <simple-int3>44</simple-int3>
+ <deep>
+ <simple-int3>4</simple-int3>
+ </deep>
+ <simple-list>4</simple-list>
+ </complex-dto-bInner>
+ <simple-list>4</simple-list>
+ </dto_d>
+ <simpleInt>44</simpleInt>
+ <simple-test>545</simple-test>
+ <simple-long>454545</simple-long>
+ <simpleBoolean>false</simpleBoolean>
+ <dto-c>
+ <dto-a-inner>
+ <dto-a-inner-inner>
+ <simple-arg>456</simple-arg>
+ </dto-a-inner-inner>
+ <simple-arg>44</simple-arg>
+ </dto-a-inner>
+ </dto-c>
+ <simple-short>4</simple-short>
+ <simple-BigInteger>999</simple-BigInteger>
+ <simple-byte>4</simple-byte>
+ <peers>
+ <port>port1</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <peers>
+ <port>port23</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test2</name>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_dep_2</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_test1</name>
+ <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+
+ <name>test1</name>
+
+ <simple-long-2>44</simple-long-2>
+ <binaryLeaf>8</binaryLeaf>
+ <binaryLeaf>1</binaryLeaf>
+ <binaryLeaf>0</binaryLeaf>
+ <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ <simple-int1>444</simple-int1>
+ <simple-int2>4444</simple-int2>
+ <simple-int3>454</simple-int3>
+ <complex-dto-bInner>
+ <simple-int3>44</simple-int3>
+ <deep>
+ <simple-int3>4</simple-int3>
+ </deep>
+ <simple-list>4</simple-list>
+ </complex-dto-bInner>
+ <simple-list>4</simple-list>
+ </dto_d>
+ <simpleInt>44</simpleInt>
+ <simple-test>545</simple-test>
+ <simple-long>454545</simple-long>
+ <simpleBoolean>false</simpleBoolean>
+ <dto-c>
+ <dto-a-inner>
+ <dto-a-inner-inner>
+ <simple-arg>456</simple-arg>
+ </dto-a-inner-inner>
+ <simple-arg>44</simple-arg>
+ </dto-a-inner>
+ </dto-c>
+ <simple-short>4</simple-short>
+ <simple-BigInteger>999</simple-BigInteger>
+ <simple-byte>4</simple-byte>
+ <peers>
+ <port>port1</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <peers>
+ <port>port23</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test2</name>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_dep_2</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_test1</name>
+ <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<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>/data/modules/module[name='impl-netconf']/instance[name='instance']</context-instance>
+ <arg1>
+ testarg1
+ </arg1>
+ </no-arg>
+</rpc>
--- /dev/null
+<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>
+ /data/modules/module[name='impl-netconf']/instance[name='instance2']/inner-running-data-additional[key='randomString_1003']
+ </context-instance>
+ </noArgInner>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <noArgInnerInner
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ <context-instance>
+ /data/modules/module[name='impl-netconf']/instance[name='instance2']/inner-running-data[key='1015']/inner-inner-running-data[key='1017']
+ </context-instance>
+
+ <arg1>
+ 456
+ </arg1>
+ <arg2>
+ true
+ </arg2>
+
+ </noArgInnerInner>
+</rpc>
--- /dev/null
+<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="merge">
+ <name>threadfactory-naming-instance</name>
+ <type
+ xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ th-java:threadfactory-naming
+ </type>
+ <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ prefixDefinedInXML
+ </name-prefix>
+ </module>
+ </modules>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ <unknownAttribute>error</unknownAttribute>
+ </module>
+
+
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+
+ </modules>
+
+ <unknownAttribute>error</unknownAttribute>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+
+ </modules>
+
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <unknownAttribute>error</unknownAttribute>
+ l
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+
+ </modules>
+
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ l
+ <service>
+ <type>testing</type>
+ <unknownAttribute>error</unknownAttribute>
+
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+
+ </modules>
+
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ l
+ <service>
+ <type>testing</type>
+
+ <instance>
+ <name>ref_dep</name>
+ <unknownAttribute>error</unknownAttribute>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <unknownAttribute>error</unknownAttribute>
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+
+ <name>test1</name>
+
+ <simple-long-2>44</simple-long-2>
+ <binaryLeaf>8</binaryLeaf>
+ <binaryLeaf>1</binaryLeaf>
+ <binaryLeaf>0</binaryLeaf>
+ <dto_d>
+ <unknownAttribute>error</unknownAttribute>
+ <simple-int1>444</simple-int1>
+ <simple-int2>4444</simple-int2>
+ <simple-int3>454</simple-int3>
+ <complex-dto-bInner>
+ <simple-int3>44</simple-int3>
+ <deep>
+ <simple-int3>4</simple-int3>
+ </deep>
+ <simple-list>4</simple-list>
+ </complex-dto-bInner>
+ <simple-list>4</simple-list>
+ </dto_d>
+ <simpleInt>44</simpleInt>
+ <simple-test>545</simple-test>
+ <simple-long>454545</simple-long>
+ <simpleBoolean>false</simpleBoolean>
+ <dto-c>
+ <dto-a-inner>
+ <dto-a-inner-inner>
+ <simple-arg>456</simple-arg>
+ </dto-a-inner-inner>
+ <simple-arg>44</simple-arg>
+ </dto-a-inner>
+ </dto-c>
+ <simple-short>4</simple-short>
+ <simple-BigInteger>999</simple-BigInteger>
+ <simple-byte>4</simple-byte>
+ <peers>
+ <port>port1</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <peers>
+ <port>port23</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test2</name>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_dep_2</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_test1</name>
+ <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <name>dep</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <name>dep2</name>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-dep
+ </type>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+
+ <name>test1</name>
+
+ <simple-long-2>44</simple-long-2>
+ <binaryLeaf>8</binaryLeaf>
+ <binaryLeaf>1</binaryLeaf>
+ <binaryLeaf>0</binaryLeaf>
+ <dto_d>
+ <simple-int1>444</simple-int1>
+ <simple-int2>4444</simple-int2>
+ <simple-int3>454</simple-int3>
+ <complex-dto-bInner>
+ <simple-int3>44</simple-int3>
+ <deep>
+ <simple-int3>4</simple-int3>
+ </deep>
+ <simple-list>4</simple-list>
+ </complex-dto-bInner>
+ <simple-list>4</simple-list>
+ </dto_d>
+ <simpleInt>44</simpleInt>
+ <simple-test>545</simple-test>
+ <simple-long>454545</simple-long>
+ <simpleBoolean>false</simpleBoolean>
+ <dto-c>
+ <dto-a-inner>
+ <dto-a-inner-inner>
+ <simple-arg>456</simple-arg>
+ </dto-a-inner-inner>
+ <simple-arg>44</simple-arg>
+ </dto-a-inner>
+ </dto-c>
+ <simple-short>4</simple-short>
+ <simple-BigInteger>999</simple-BigInteger>
+ <simple-byte>4</simple-byte>
+ <peers>
+ <port>port1</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ </peers>
+ <peers>
+ <port>port23</port>
+ <simple-int3>456</simple-int3>
+ <core-size>44</core-size>
+ <unknownAttribute>error</unknownAttribute>
+ </peers>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+ <name>test2</name>
+ <testing-dep>
+ <type>testing</type>
+ <name>ref_dep</name>
+ </testing-dep>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type>testing</type>
+ <instance>
+ <name>ref_dep</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_dep_2</name>
+ <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ </provider>
+ </instance>
+ <instance>
+ <name>ref_test1</name>
+ <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </config>
+ </edit-config>
+</rpc>
--- /dev/null
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+ <validate>
+ <source>
+ <candidate/>
+ </source>
+ </validate>
+</rpc>
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.1-SNAPSHOT</version>
+ <relativePath>../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.1-SNAPSHOT</version>
+ <artifactId>netconf-subsystem</artifactId>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <prerequisites>
+ <maven>3.0.4</maven>
+ </prerequisites>
+
+
+ <modules>
+ <module>netconf-api</module>
+ <module>netconf-impl</module>
+ <module>config-netconf-connector</module>
+ <module>netconf-util</module>
+ <module>netconf-it</module>
+ <module>config-persister-impl</module>
+ <module>netconf-mapping-api</module>
+ <module>netconf-client</module>
+ </modules>
+
+ <properties>
+ <osgi.version>5.0.0</osgi.version>
+ <maven.bundle.version>2.3.7</maven.bundle.version>
+ <slf4j.version>1.7.2</slf4j.version>
+ <java.version.source>1.7</java.version.source>
+ <java.version.target>1.7</java.version.target>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>${osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <version>${bgpcep.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-api</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-manager</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-jmx-generator</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-util</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-store-api</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-store-impl</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-test</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <configuration>
+ <source>${java.version.source}</source>
+ <target>${java.version.target}</target>
+ <testSource>${java.version.source}</testSource>
+ <testTarget>${java.version.target}</testTarget>
+ </configuration>
+ </plugin>
+ </plugins>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${maven.bundle.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+
+ </pluginManagement>
+ </build>
+ <!--
+ <repositories>
+ <repository>
+ <id>opendaylight-snapshot</id>
+ <url>http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot</url>
+ <snapshots>
+ <updatePolicy>daily</updatePolicy>
+ </snapshots>
+ </repository>
+ </repositories>
+ -->
+</project>
<!-- md-sal -->
<module>opendaylight/md-sal</module>
<module>opendaylight/config</module>
+ <module>opendaylight/netconf</module>
<!-- config -->
<!-- Web bundles -->