public interface ConfigSnapshotHolder {
- /**
- * Get part of get-config document that contains just
- */
- String getConfigSnapshot();
+ /**
+ * Get part of get-config document that contains just
+ */
+ String getConfigSnapshot();
- /**
- * Get only required capabilities referenced by the snapshot.
- */
- SortedSet<String> getCapabilities();
- }
+ /**
+ * Get only required capabilities referenced by the snapshot.
+ */
+ SortedSet<String> getCapabilities();
+
+}
--- /dev/null
+package org.opendaylight.controller.config.persist.api;
+
+import java.util.SortedSet;
+
+public class ConfigSnapshotHolderImpl implements ConfigSnapshotHolder {
+
+ private final String snapshot;
+ private final SortedSet<String> caps;
+ private final String fileName;
+
+ public ConfigSnapshotHolderImpl(String configSnapshot, SortedSet<String> capabilities, String fileName) {
+ this.snapshot = configSnapshot;
+ this.caps = capabilities;
+ this.fileName = fileName;
+ }
+
+ @Override
+ public String getConfigSnapshot() {
+ return snapshot;
+ }
+
+ @Override
+ public SortedSet<String> getCapabilities() {
+ return caps;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ @Override
+ public String toString() {
+ return "ConfigSnapshotHolderImpl{" +
+ "snapshot='" + snapshot + '\'' +
+ ", caps=" + caps +
+ ", fileName='" + fileName + '\'' +
+ '}';
+ }
+}
package org.opendaylight.controller.config.persist.api;
-import com.google.common.base.Optional;
-
import java.io.IOException;
+import java.util.List;
/**
* Base interface for persister implementation.
void persistConfig(ConfigSnapshotHolder configSnapshotHolder) throws IOException;
- Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException;
+ List<ConfigSnapshotHolder> loadLastConfigs() throws IOException;
@Override
void close();
package org.opendaylight.controller.config.persist.storage.directory;
import com.google.common.base.Charsets;
-import com.google.common.base.Optional;
import com.google.common.io.Files;
import org.apache.commons.io.IOUtils;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolderImpl;
import org.opendaylight.controller.config.persist.api.Persister;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
}
@Override
- public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
File[] filesArray = storage.listFiles();
- if (filesArray.length == 0) {
- return Optional.absent();
+ if (filesArray == null || filesArray.length == 0) {
+ return Collections.emptyList();
}
List<File> sortedFiles = new ArrayList<>(Arrays.asList(filesArray));
Collections.sort(sortedFiles);
// combine all found files
+ logger.debug("Reading files in following order: {}", sortedFiles);
- SortedSet<String> combinedCapabilities = new TreeSet<>();
- StringBuilder modulesBuilder = new StringBuilder(), servicesBuilder = new StringBuilder();
+ List<ConfigSnapshotHolder> result = new ArrayList<>();
for (File file : sortedFiles) {
logger.trace("Adding file '{}' to combined result", file);
final MyLineProcessor lineProcessor = new MyLineProcessor(file.getAbsolutePath());
Files.readLines(file, ENCODING, lineProcessor);
-
- modulesBuilder.append(lineProcessor.getModules());
- servicesBuilder.append(lineProcessor.getServices());
- combinedCapabilities.addAll(lineProcessor.getCapabilities());
+ result.add(lineProcessor.getConfigSnapshotHolder(header, middle, footer));
}
- String combinedSnapshot = header + modulesBuilder.toString() + middle + servicesBuilder.toString() + footer;
- ConfigSnapshotHolder result = new ConfigSnapshotHolderImpl(combinedSnapshot, combinedCapabilities);
- return Optional.of(result);
+ return result;
}
return caps;
}
-}
-
-class ConfigSnapshotHolderImpl implements ConfigSnapshotHolder {
-
- private final String snapshot;
- private final SortedSet<String> caps;
-
- public ConfigSnapshotHolderImpl(String configSnapshot, SortedSet<String> capabilities) {
- this.snapshot = configSnapshot;
- this.caps = capabilities;
- }
-
- @Override
- public String getConfigSnapshot() {
- return snapshot;
+ ConfigSnapshotHolder getConfigSnapshotHolder(String header, String middle, String footer) {
+ String combinedSnapshot = header + getModules() + middle + getServices() + footer;
+ ConfigSnapshotHolder result = new ConfigSnapshotHolderImpl(combinedSnapshot, getCapabilities(), fileNameForReporting);
+ return result;
}
- @Override
- public SortedSet<String> getCapabilities() {
- return caps;
- }
}
+
package org.opendaylight.controller.config.persist.storage.directory;
-import com.google.common.base.Optional;
import org.apache.commons.io.IOUtils;
-import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import java.io.File;
-import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
public class DirectoryStorageAdapterTest {
DirectoryPersister tested;
- SortedSet<String> expectedCapabilities;
- String expectedSnapshot;
-
- @Before
- public void setUp() throws Exception {
- expectedCapabilities = new TreeSet<>(IOUtils.readLines(getClass().getResourceAsStream("/expectedCapabilities.txt")));
- expectedSnapshot = IOUtils.toString(getClass().getResourceAsStream("/expectedSnapshot.xml"));
- }
@Test
public void testEmptyDirectory() throws Exception {
File folder = new File("target/emptyFolder");
folder.mkdir();
tested = new DirectoryPersister((folder));
- assertEquals(Optional.<ConfigSnapshotHolder>absent(), tested.loadLastConfig());
+ assertEquals(Collections.<ConfigSnapshotHolder>emptyList(), tested.loadLastConfigs());
try {
tested.persistConfig(new ConfigSnapshotHolder() {
public void testOneFile() throws Exception {
File folder = getFolder("oneFile");
tested = new DirectoryPersister((folder));
- assertExpected();
+ List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
+ assertEquals(1, results.size());
+ ConfigSnapshotHolder result = results.get(0);
+ assertSnapshot(result, "oneFileExpected");
}
- private void assertExpected() throws IOException {
- Optional<ConfigSnapshotHolder> maybeResult = tested.loadLastConfig();
- assertTrue(maybeResult.isPresent());
- ConfigSnapshotHolder result = maybeResult.get();
- assertEquals(expectedCapabilities, result.getCapabilities());
- assertEquals(expectedSnapshot, result.getConfigSnapshot());
- }
@Test
public void testTwoFiles() throws Exception {
File folder = getFolder("twoFiles");
tested = new DirectoryPersister((folder));
- assertExpected();
+ List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
+ assertEquals(2, results.size());
+ assertSnapshot(results.get(0), "twoFilesExpected1");
+ assertSnapshot(results.get(1), "twoFilesExpected2");
+ }
+
+ private void assertSnapshot(ConfigSnapshotHolder result, String directory) throws Exception {
+ SortedSet<String> expectedCapabilities = new TreeSet<>(IOUtils.readLines(getClass().getResourceAsStream("/" + directory + "/expectedCapabilities.txt")));
+ String expectedSnapshot = IOUtils.toString(getClass().getResourceAsStream("/" + directory + "/expectedSnapshot.xml"));
+ assertEquals(expectedCapabilities, result.getCapabilities());
+ assertEquals(expectedSnapshot, result.getConfigSnapshot());
}
}
--- /dev/null
+urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
--- /dev/null
+<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
+ <name>hash-map-data-store</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
+ <name>dom-broker</name>
+ <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <name>ref_hash-map-data-store</name>
+ </data-store>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
+ <name>binding-broker-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <name>ref_binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
+ <name>binding-notification-broker</name>
+ </module>
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <instance>
+ <name>ref_yang-schema-service</name>
+ <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <instance>
+ <name>ref_binding-notification-broker</name>
+ <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <instance>
+ <name>ref_hash-map-data-store</name>
+ <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+ <instance>
+ <name>ref_binding-broker-impl</name>
+ <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
+ <instance>
+ <name>ref_runtime-mapping-singleton</name>
+ <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <instance>
+ <name>ref_dom-broker</name>
+ <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
+ </instance>
+ </service>
+ </services>
+</data>
--- /dev/null
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
+urn:ietf:params:netconf:capability:candidate:1.0
+urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
+urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
+urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
+urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
+urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
+urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
+urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
--- /dev/null
+<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
+ <name>binding-data-broker</name>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>ref_dom-broker</name>
+ </dom-broker>
+ <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>ref_runtime-mapping-singleton</name>
+ </mapping-service>
+ </module>
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <instance>
+ <name>ref_binding-data-broker</name>
+ <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
+ </instance>
+ </service>
+ </services>
+</data>
import com.google.common.io.Files;
import org.apache.commons.lang3.StringUtils;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolderImpl;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.api.PropertiesProvider;
import org.opendaylight.controller.config.persist.api.StorageAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.xml.sax.SAXException;
-import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
} else {
numberOfStoredBackups = Integer.MAX_VALUE;
}
-
+ logger.trace("Property {} set to {}", NUMBER_OF_BACKUPS, numberOfStoredBackups);
return result;
}
}
@Override
- public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
Preconditions.checkNotNull(storage, "Storage file is null");
if (!storage.exists()) {
- return Optional.absent();
+ return Collections.emptyList();
}
final LineProcessor lineProcessor = new LineProcessor();
- String result = Files.readLines(storage, ENCODING, lineProcessor);
-
- try {
- if (lineProcessor.getConfigSnapshot().isPresent() == false) {
- return Optional.absent();
- } else {
- return Optional.<ConfigSnapshotHolder> of(new PersistedConfigImpl(lineProcessor.getConfigSnapshot(),
- lineProcessor.getCapabilities()));
- }
+ Files.readLines(storage, ENCODING, lineProcessor);
- } catch (ParserConfigurationException | SAXException e) {
- throw new IOException("Unable to load last config ", e);
+ if (lineProcessor.getConfigSnapshot().isPresent() == false) {
+ return Collections.emptyList();
+ } else {
+ return Arrays.<ConfigSnapshotHolder>asList(new ConfigSnapshotHolderImpl(lineProcessor.getConfigSnapshot().get(),
+ lineProcessor.getCapabilities(), storage.getAbsolutePath()));
}
+
}
private static final class LineProcessor implements com.google.common.io.LineProcessor<String> {
return true;
}
- Optional<String> getConfigSnapshot() throws IOException, SAXException, ParserConfigurationException {
+ Optional<String> getConfigSnapshot() {
final String xmlContent = snapshotBuffer.toString();
- if (xmlContent == null || xmlContent.equals("")) {
+ if (xmlContent.equals("")) {
return Optional.absent();
- } else
+ } else {
return Optional.of(xmlContent);
+ }
}
- SortedSet<String> getCapabilities() throws IOException, SAXException, ParserConfigurationException {
+ SortedSet<String> getCapabilities() {
return caps;
}
return "FileStorageAdapter [storage=" + storage + "]";
}
- private class PersistedConfigImpl implements ConfigSnapshotHolder {
-
- private final String snapshot;
- private final SortedSet<String> caps;
-
- public PersistedConfigImpl(Optional<String> configSnapshot, SortedSet<String> capabilities) {
- this.snapshot = configSnapshot.get();
- this.caps = capabilities;
- }
-
- @Override
- public String getConfigSnapshot() {
- return snapshot;
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return caps;
- }
- }
-
}
package org.opendaylight.controller.config.persist.storage.file;
import com.google.common.base.Charsets;
-import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import org.junit.Before;
import java.io.File;
import java.nio.file.Files;
import java.util.Collection;
+import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
});
assertEquals(14, readLines.size());
- Optional<ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
- assertTrue(lastConf.isPresent());
+ List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
+ assertEquals(1, lastConf.size());
+ ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
assertEquals("<config>2</config>",
- lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
- assertEquals(createCaps(), lastConf.get().getCapabilities());
+ configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
+ assertEquals(createCaps(), configSnapshotHolder.getCapabilities());
}
private SortedSet<String> createCaps() {
});
assertEquals(7, readLines.size());
- Optional<ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
- assertTrue(lastConf.isPresent());
+ List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
+ assertEquals(1, lastConf.size());
+ ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
assertEquals("<config>2</config>",
- lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
+ configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
}
@Test
assertEquals(14, readLines.size());
- Optional<ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
- assertTrue(lastConf.isPresent());
+ List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
+ assertEquals(1, lastConf.size());
+ ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
assertEquals("<config>3</config>",
- lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
+ configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
assertFalse(readLines.contains(holder.getConfigSnapshot()));
}
FileStorageAdapter storage = new FileStorageAdapter();
storage.setFileStorage(file);
- Optional<ConfigSnapshotHolder> elementOptional = storage.loadLastConfig();
- assertThat(elementOptional.isPresent(), is(false));
+ List<ConfigSnapshotHolder> elementOptional = storage.loadLastConfigs();
+ assertThat(elementOptional.size(), is(0));
}
@Test(expected = NullPointerException.class)
public void testNoProperties() throws Exception {
FileStorageAdapter storage = new FileStorageAdapter();
- storage.loadLastConfig();
+ storage.loadLastConfigs();
}
@Test(expected = NullPointerException.class)
*/
package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.regex.Pattern;
-
+import com.google.common.annotations.VisibleForTesting;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.AnnotationsDirective;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.ConstructorsDirective;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.annotations.VisibleForTesting;
-
-import freemarker.template.Configuration;
-import freemarker.template.Template;
-import freemarker.template.TemplateException;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
public class FtlFilePersister {
private static final Logger logger = LoggerFactory
ftlFile.getFtlTempleteLocation());
try {
template.process(ftlFile, writer);
- } catch (TemplateException e) {
+ } catch (Throwable e) {
throw new IllegalStateException(
"Template error while generating " + ftlFile, e);
}
import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute.Dependency;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.Dependency;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.VoidAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation.Parameter;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
+import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType;
import org.opendaylight.yangtools.sal.binding.model.api.Type;
import javax.management.openmbean.SimpleType;
// convert attributes to getters
for (AttributeIfc attributeIfc : entry.getAttributes()) {
- String returnType = null;
- returnType = getReturnType(entry, attributeIfc);
+ String returnType;
+ returnType = getReturnType(attributeIfc);
String getterName = "get"
+ attributeIfc.getUpperCaseCammelCase();
MethodDeclaration getter = new MethodDeclaration(returnType,
fields.add(field);
}
MethodDeclaration operation = new MethodDeclaration(
- getReturnType(entry, rpc.getReturnType()), rpc.getName(), fields);
+ getReturnType(rpc.getReturnType()), rpc.getName(), fields);
methods.add(operation);
}
return result;
}
- private static String getReturnType(RuntimeBeanEntry entry, AttributeIfc attributeIfc) {
+ // FIXME: put into Type.toString
+ static String serializeType(Type type) {
+ if (type instanceof ParameterizedType){
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ StringBuffer sb = new StringBuffer();
+ sb.append(parameterizedType.getRawType().getFullyQualifiedName());
+ sb.append("<");
+ boolean first = true;
+ for(Type parameter: parameterizedType.getActualTypeArguments()) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(serializeType(parameter));
+ }
+ sb.append(">");
+ return sb.toString();
+ } else {
+ return type.getFullyQualifiedName();
+ }
+ }
+
+
+ private static String getReturnType(AttributeIfc attributeIfc) {
String returnType;
if (attributeIfc instanceof TypedAttribute) {
- returnType = ((TypedAttribute) attributeIfc).getType()
- .getFullyQualifiedName();
- } else if (attributeIfc instanceof TOAttribute) {
- String fullyQualifiedName = FullyQualifiedNameHelper
- .getFullyQualifiedName(entry.getPackageName(),
- attributeIfc.getUpperCaseCammelCase());
-
- returnType = fullyQualifiedName;
- } else if (attributeIfc instanceof ListAttribute) {
- AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
- .getInnerAttribute();
-
- String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr)
- .getType().getFullyQualifiedName()
- : FullyQualifiedNameHelper.getFullyQualifiedName(
- entry.getPackageName(),
- attributeIfc.getUpperCaseCammelCase());
-
- returnType = "java.util.List<" + innerTpe + ">";
+ Type type = ((TypedAttribute) attributeIfc).getType();
+ returnType = serializeType(type);
} else if (attributeIfc == VoidAttribute.getInstance()) {
return "void";
} else {
public static GeneralInterfaceTemplate mXBeanInterfaceTemplateFromMbe(
ModuleMXBeanEntry mbe) {
MXBeanInterfaceAttributesProcessor attrProcessor = new MXBeanInterfaceAttributesProcessor();
- attrProcessor.processAttributes(mbe.getAttributes(),
- mbe.getPackageName());
+ attrProcessor.processAttributes(mbe.getAttributes());
GeneralInterfaceTemplate ifcTemplate = new GeneralInterfaceTemplate(
getHeaderFromEntry(mbe), mbe.getPackageName(),
mbe.getMXBeanInterfaceName(), Lists.<String> newArrayList(),
ModuleMXBeanEntry mbe) {
Map<String, GeneralClassTemplate> retVal = Maps.newHashMap();
TOAttributesProcessor processor = new TOAttributesProcessor();
- processor.processAttributes(mbe.getAttributes(), mbe.getPackageName());
+ processor.processAttributes(mbe.getAttributes());
for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor
.getTOs()) {
List<Constructor> constructors = Lists.newArrayList();
yangPropertiesToTypesMap.put(returnType.getAttributeYangName(), returnType);
}
- processor.processAttributes(yangPropertiesToTypesMap, rbe.getPackageName());
+ processor.processAttributes(yangPropertiesToTypesMap);
for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor
.getTOs()) {
List<Constructor> constructors = Lists.newArrayList();
private final List<TOInternal> tos = Lists.newArrayList();
- void processAttributes(Map<String, AttributeIfc> attributes,
- String packageName) {
+ void processAttributes(Map<String, AttributeIfc> attributes) {
for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
AttributeIfc attributeIfc = attrEntry.getValue();
if (attributeIfc instanceof TOAttribute) {
- createTOInternal(packageName, attributeIfc);
+ createTOInternal((TOAttribute) attributeIfc);
}
if (attributeIfc instanceof ListAttribute) {
AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
.getInnerAttribute();
if (innerAttr instanceof TOAttribute) {
- createTOInternal(packageName, innerAttr);
+ createTOInternal((TOAttribute) innerAttr);
}
}
}
}
- private void createTOInternal(String packageName,
- AttributeIfc attributeIfc) {
- String fullyQualifiedName = FullyQualifiedNameHelper
- .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase());
+ private void createTOInternal(TOAttribute toAttribute) {
- String type = fullyQualifiedName;
- String name = attributeIfc.getUpperCaseCammelCase();
- Map<String, AttributeIfc> attrs = ((TOAttribute) attributeIfc)
- .getCapitalizedPropertiesToTypesMap();
- // recursive processing
- processAttributes(attrs, packageName);
+ Map<String, AttributeIfc> attrs = toAttribute.getCapitalizedPropertiesToTypesMap();
+ // recursive processing of TO's attributes
+ processAttributes(attrs);
- tos.add(new TOInternal(type, name, attrs, packageName));
+ tos.add(new TOInternal(toAttribute.getType(), attrs));
}
List<TOInternal> getTOs() {
}
private static class TOInternal {
- private final String type, name;
+ private final String fullyQualifiedName, name;
private List<Field> fields;
private List<MethodDefinition> methods;
- public TOInternal(String type, String name,
+ public TOInternal(Type type, Map<String, AttributeIfc> attrs) {
+ this(type.getFullyQualifiedName(), type.getName(), attrs, type.getPackageName());
+ }
+
+ public TOInternal(String fullyQualifiedName, String name,
Map<String, AttributeIfc> attrs, String packageName) {
- super();
- this.type = type;
+ this.fullyQualifiedName = fullyQualifiedName;
this.name = name;
processAttrs(attrs, packageName);
}
- private void processAttrs(Map<String, AttributeIfc> attrs,
- String packageName) {
+ private void processAttrs(Map<String, AttributeIfc> attrs, String packageName) {
fields = Lists.newArrayList();
methods = Lists.newArrayList();
String varName = BindingGeneratorUtil
.parseToValidParamName(attrEntry.getKey());
- String fullyQualifiedName = null;
+ String fullyQualifiedName;
if (attrEntry.getValue() instanceof TypedAttribute) {
- Type innerType = ((TypedAttribute) attrEntry.getValue())
- .getType();
- fullyQualifiedName = innerType.getFullyQualifiedName();
- } else if (attrEntry.getValue() instanceof ListAttribute) {
- AttributeIfc innerAttr = ((ListAttribute) attrEntry
- .getValue()).getInnerAttribute();
-
- String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr)
- .getType().getFullyQualifiedName()
- : FullyQualifiedNameHelper
- .getFullyQualifiedName(packageName, attrEntry.getValue().getUpperCaseCammelCase());
-
- fullyQualifiedName = "java.util.List<" + innerTpe + ">";
- } else
+ Type type = ((TypedAttribute) attrEntry.getValue()).getType();
+ fullyQualifiedName = serializeType(type);
+ } else {
fullyQualifiedName = FullyQualifiedNameHelper
.getFullyQualifiedName(packageName, attrEntry.getValue().getUpperCaseCammelCase());
-
+ }
fields.add(new Field(fullyQualifiedName, varName));
String getterName = "get" + innerName;
}
String getType() {
- return type;
+ return fullyQualifiedName;
}
String getName() {
}
private static class MXBeanInterfaceAttributesProcessor {
- private static final String STRING_FULLY_QUALIFIED_NAME = "java.util.List";
private final List<MethodDeclaration> methods = Lists.newArrayList();
- void processAttributes(Map<String, AttributeIfc> attributes,
- String packageName) {
+ void processAttributes(Map<String, AttributeIfc> attributes) {
for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
String returnType;
AttributeIfc attributeIfc = attrEntry.getValue();
if (attributeIfc instanceof TypedAttribute) {
- returnType = ((TypedAttribute) attributeIfc).getType()
- .getFullyQualifiedName();
- } else if (attributeIfc instanceof TOAttribute) {
- String fullyQualifiedName = FullyQualifiedNameHelper
- .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase());
-
- returnType = fullyQualifiedName;
- } else if (attributeIfc instanceof ListAttribute) {
- String fullyQualifiedName = null;
-
- AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
- .getInnerAttribute();
- if (innerAttr instanceof JavaAttribute) {
- fullyQualifiedName = ((JavaAttribute) innerAttr)
- .getType().getFullyQualifiedName();
- } else if (innerAttr instanceof TOAttribute) {
- fullyQualifiedName = FullyQualifiedNameHelper
- .getFullyQualifiedName(packageName, innerAttr.getUpperCaseCammelCase());
- }
-
- returnType = STRING_FULLY_QUALIFIED_NAME.concat("<")
- .concat(fullyQualifiedName).concat(">");
+ TypedAttribute typedAttribute = (TypedAttribute) attributeIfc;
+ returnType = serializeType(typedAttribute.getType());
} else {
throw new UnsupportedOperationException(
"Attribute not supported: "
AttributeIfc attributeIfc = attrEntry.getValue();
if (attributeIfc instanceof TypedAttribute) {
- type = ((TypedAttribute) attributeIfc).getType()
- .getFullyQualifiedName();
+ TypedAttribute typedAttribute = (TypedAttribute) attributeIfc;
+ type = serializeType(typedAttribute.getType());
} else if (attributeIfc instanceof TOAttribute) {
String fullyQualifiedName = FullyQualifiedNameHelper
.getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase());
type = fullyQualifiedName;
- } else if (attributeIfc instanceof ListAttribute) {
+ } else if (attributeIfc instanceof ListAttribute) { //FIXME: listAttribute might extend TypedAttribute
String fullyQualifiedName = null;
AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
.getInnerAttribute();
AttributeIfc attributeIfc = attrEntry.getValue();
if (attributeIfc instanceof TypedAttribute) {
- type = ((TypedAttribute) attributeIfc).getType()
- .getFullyQualifiedName();
+ TypedAttribute typedAttribute = (TypedAttribute) attributeIfc;
+ type = serializeType(typedAttribute.getType());
} else if (attributeIfc instanceof TOAttribute) {
String fullyQualifiedName = FullyQualifiedNameHelper
.getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase());
List<Annotation> annotations = Lists
.newArrayList(overrideAnnotation);
- if (attributeIfc instanceof DependencyAttribute) {
+ if (attributeIfc instanceof AbstractDependencyAttribute) {
isDependency = true;
- dependency = ((DependencyAttribute) attributeIfc)
+ dependency = ((AbstractDependencyAttribute) attributeIfc)
.getDependency();
annotations.add(Annotation
.createRequireIfcAnnotation(dependency.getSie()));
*/
package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.Dependency;
+
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute.Dependency;
-
public class ModuleField extends Field {
private final String nullableDefault, attributeName;
String attributeName, String nullableDefault, boolean isDependency,
Dependency dependency) {
super(modifiers, type, name);
- this.nullableDefault = nullableDefault;
this.dependent = isDependency;
this.dependency = dependency;
this.attributeName = attributeName;
+ if (type.startsWith(List.class.getName()) && nullableDefault == null) {
+ String generics = type.substring(List.class.getName().length());
+ nullableDefault = "new " + ArrayList.class.getName() + generics + "()";
+ }
+ this.nullableDefault = nullableDefault;
}
public ModuleField(String type, String name, String attributeName,
public String getAttributeName() {
return attributeName;
}
+
}
public void validate(){
<#list moduleFields as field>
<#if field.dependent==true && field.dependency.mandatory==true>
+ <#if field.type?starts_with("java.util.List")>
+ for(javax.management.ObjectName dep : ${field.name}) {
+ dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, dep, ${field.name}JmxAttribute);
+ }
+ <#else>
dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, ${field.name}, ${field.name}JmxAttribute);
</#if>
+ </#if>
</#list>
customValidation();
}
// caches of resolved dependencies
<#list moduleFields as field>
<#if field.dependent==true>
+ <#if field.type?starts_with("java.util.List")>
+ private java.util.List<${field.dependency.sie.exportedOsgiClassName}> ${field.name}Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>();
+ protected final java.util.List<${field.dependency.sie.exportedOsgiClassName}> get${field.attributeName}Dependency(){
+ return ${field.name}Dependency;
+ }
+ <#else>
private ${field.dependency.sie.exportedOsgiClassName} ${field.name}Dependency;
protected final ${field.dependency.sie.exportedOsgiClassName} get${field.attributeName}Dependency(){
return ${field.name}Dependency;
}
+ </#if>
</#if>
</#list>
<#list moduleFields as field>
<#if field.dependent==true>
-
<#if field.dependency.mandatory==false>
if(${field.name}!=null) {
</#if>
- ${field.name}Dependency = dependencyResolver.resolveInstance(${field.dependency.sie.exportedOsgiClassName}.class, ${field.name}, ${field.name}JmxAttribute);
+ <#if field.type?starts_with("java.util.List")>
+ ${field.name}Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>();
+ for(javax.management.ObjectName dep : ${field.name}) {
+ ${field.name}Dependency.add(dependencyResolver.resolveInstance(${field.dependency.sie.exportedOsgiClassName}.class, dep, ${field.name}JmxAttribute));
+ }
+ <#else>
+ ${field.name}Dependency = dependencyResolver.resolveInstance(${field.dependency.sie.exportedOsgiClassName}.class, ${field.name}, ${field.name}JmxAttribute);
+ </#if>
<#if field.dependency.mandatory==false>
}
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+//TODO: refactor
public class JMXGeneratorTest extends AbstractGeneratorTest {
JMXGenerator jmxGenerator;
File generatedResourcesDir;
private static final List<String> expectedModuleFileNames = ServiceInterfaceEntryTest
- .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, " +
- "AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, " +
- "AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, " +
- "AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, " +
- "AsyncEventBusModule.java, AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, " +
- "AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, " +
- "AsyncEventBusRuntimeRegistrator.java, DynamicThreadPoolModule.java, " +
- "DynamicThreadPoolModuleFactory.java, DynamicThreadPoolModuleMXBean.java, " +
- "DynamicThreadPoolRuntimeMXBean.java, DynamicThreadPoolRuntimeRegistration.java, " +
- "DynamicThreadPoolRuntimeRegistrator.java, EventBusModule.java, EventBusModuleFactory.java, " +
- "EventBusModuleMXBean.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, " +
- "InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, " +
- "NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, Peer.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]");
+ .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, AbstractThreadPoolRegistryImplModule.java, AbstractThreadPoolRegistryImplModuleFactory.java, AsyncEventBusModule.java, AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, AsyncEventBusRuntimeRegistrator.java, DynamicThreadPoolModule.java, DynamicThreadPoolModuleFactory.java, DynamicThreadPoolModuleMXBean.java, DynamicThreadPoolRuntimeMXBean.java, DynamicThreadPoolRuntimeRegistration.java, DynamicThreadPoolRuntimeRegistrator.java, EventBusModule.java, EventBusModuleFactory.java, EventBusModuleMXBean.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, Peer.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, ThreadPoolRegistryImplModule.java, ThreadPoolRegistryImplModuleFactory.java, ThreadPoolRegistryImplModuleMXBean.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]");
private static final List<String> expectedBGPNames = ServiceInterfaceEntryTest
.toFileNames("[AbstractBgpListenerImplModule.java, " + "AbstractBgpListenerImplModuleFactory.java, " +
"NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, " +
"NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java]");
private static final List<String> expectedAllFileNames = ServiceInterfaceEntryTest
- .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, " +
- "AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, " +
- "AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, " +
- "AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, " +
- "AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, " +
- "AbstractNetconfTestFileImplModule.java, AbstractNetconfTestFileImplModuleFactory.java, " +
- "AbstractNetconfTestFiles1ImplModule.java, AbstractNetconfTestFiles1ImplModuleFactory.java, " +
- "AbstractNetconfTestImplModule.java, AbstractNetconfTestImplModuleFactory.java, " +
- "AbstractTestFileImplModule.java, AbstractTestFileImplModuleFactory.java, " +
- "AbstractTestFiles1ImplModule.java, AbstractTestFiles1ImplModuleFactory.java, " +
- "AbstractTestImplModule.java, AbstractTestImplModuleFactory.java, AsyncEventBusModule.java, " +
- "AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, " +
- "AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, " +
- "AsyncEventBusRuntimeRegistrator.java, AutoCloseableServiceInterface.java, " +
- "BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java," +
- " BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, " +
- "BgpListenerImplRuntimeRegistrator.java, ComplexDtoBInner.java, ComplexList.java, Deep.java, " +
- "DtoA.java, DtoA.java, DtoA.java, DtoA1.java, DtoAInner.java, DtoAInnerInner.java, DtoB.java, " +
- "DtoC.java, DynamicThreadPoolModule.java, DynamicThreadPoolModuleFactory.java, " +
- "DynamicThreadPoolModuleMXBean.java, DynamicThreadPoolRuntimeMXBean.java, " +
- "DynamicThreadPoolRuntimeRegistration.java, DynamicThreadPoolRuntimeRegistrator.java, " +
- "EventBusModule.java, EventBusModuleFactory.java, EventBusModuleMXBean.java, " +
- "EventBusServiceInterface.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, " +
- "InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, " +
- "NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, " +
- "NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, " +
- "NetconfTestFileImplModule.java, NetconfTestFileImplModuleFactory.java, " +
- "NetconfTestFileImplModuleMXBean.java, NetconfTestFileImplRuntimeMXBean.java, " +
- "NetconfTestFileImplRuntimeRegistration.java, NetconfTestFileImplRuntimeRegistrator.java, " +
- "NetconfTestFiles1ImplModule.java, NetconfTestFiles1ImplModuleFactory.java, " +
- "NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, " +
- "NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, NetconfTestImplModule.java, NetconfTestImplModuleFactory.java, NetconfTestImplModuleMXBean.java, NetconfTestImplRuntimeMXBean.java, NetconfTestImplRuntimeRegistration.java, NetconfTestImplRuntimeRegistrator.java, Peer.java, Peer.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java, ScheduledThreadPoolServiceInterface.java, SimpleList.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java, TestImplModule.java, TestImplModuleFactory.java, TestImplModuleMXBean.java, TestImplRuntimeMXBean.java, TestImplRuntimeRegistration.java, TestImplRuntimeRegistrator.java, ThreadFactoryServiceInterface.java, ThreadPoolServiceInterface.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]");
+ .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, AbstractNetconfTestFileImplModule.java, AbstractNetconfTestFileImplModuleFactory.java, AbstractNetconfTestFiles1ImplModule.java, AbstractNetconfTestFiles1ImplModuleFactory.java, AbstractNetconfTestImplModule.java, AbstractNetconfTestImplModuleFactory.java, AbstractTestFileImplModule.java, AbstractTestFileImplModuleFactory.java, AbstractTestFiles1ImplModule.java, AbstractTestFiles1ImplModuleFactory.java, AbstractTestImplModule.java, AbstractTestImplModuleFactory.java, AbstractThreadPoolRegistryImplModule.java, AbstractThreadPoolRegistryImplModuleFactory.java, AsyncEventBusModule.java, AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, AsyncEventBusRuntimeRegistrator.java, AutoCloseableServiceInterface.java, BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java, BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, BgpListenerImplRuntimeRegistrator.java, ComplexDtoBInner.java, ComplexList.java, Deep.java, DtoA.java, DtoA.java, DtoA.java, DtoA1.java, DtoAInner.java, DtoAInnerInner.java, DtoB.java, DtoC.java, DynamicThreadPoolModule.java, DynamicThreadPoolModuleFactory.java, DynamicThreadPoolModuleMXBean.java, DynamicThreadPoolRuntimeMXBean.java, DynamicThreadPoolRuntimeRegistration.java, DynamicThreadPoolRuntimeRegistrator.java, EventBusModule.java, EventBusModuleFactory.java, EventBusModuleMXBean.java, EventBusServiceInterface.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, NetconfTestFileImplModule.java, NetconfTestFileImplModuleFactory.java, NetconfTestFileImplModuleMXBean.java, NetconfTestFileImplRuntimeMXBean.java, NetconfTestFileImplRuntimeRegistration.java, NetconfTestFileImplRuntimeRegistrator.java, NetconfTestFiles1ImplModule.java, NetconfTestFiles1ImplModuleFactory.java, NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, NetconfTestImplModule.java, NetconfTestImplModuleFactory.java, NetconfTestImplModuleMXBean.java, NetconfTestImplRuntimeMXBean.java, NetconfTestImplRuntimeRegistration.java, NetconfTestImplRuntimeRegistrator.java, Peer.java, Peer.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java, ScheduledThreadPoolServiceInterface.java, SimpleList.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java, TestImplModule.java, TestImplModuleFactory.java, TestImplModuleMXBean.java, TestImplRuntimeMXBean.java, TestImplRuntimeRegistration.java, TestImplRuntimeRegistrator.java, ThreadFactoryServiceInterface.java, ThreadPoolRegistryImplModule.java, ThreadPoolRegistryImplModuleFactory.java, ThreadPoolRegistryImplModuleMXBean.java, ThreadPoolServiceInterface.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]");
private static final List<String> expectedGenerateMBEsListNames = ServiceInterfaceEntryTest
.toFileNames("[AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java, BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, BgpListenerImplRuntimeRegistrator.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java]");
PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+ ".threads.java.DynamicThreadPoolModuleFactory",//
PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
- + ".threads.java.NamingThreadFactoryModuleFactory");
+ + ".threads.java.NamingThreadFactoryModuleFactory", //
+ PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+ + ".threads.java.ThreadPoolRegistryImplModuleFactory");
+
+
assertThat(lines, equalTo(expectedLines));
}
package org.opendaylight.controller.config.yangjmxgenerator;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
import com.google.common.collect.Sets;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
moduleLocalNameFromXPath);
yangToAttributes = fillConfiguration(choiceCaseNode,
currentModule, typeProviderWrapper,
- qNamesToSIEs, schemaContext);
+ qNamesToSIEs, schemaContext, packageName);
checkUniqueAttributesWithGeneratedClass(
uniqueGeneratedClassesNames, when.getQName(),
yangToAttributes);
ChoiceCaseNode choiceCaseNode, Module currentModule,
TypeProviderWrapper typeProviderWrapper,
Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
- SchemaContext schemaContext) {
+ SchemaContext schemaContext, String packageName) {
Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
for (DataSchemaNode attrNode : choiceCaseNode.getChildNodes()) {
AttributeIfc attributeValue = getAttributeValue(attrNode,
currentModule, qNamesToSIEs, typeProviderWrapper,
- schemaContext);
+ schemaContext, packageName);
yangToAttributes.put(attributeValue.getAttributeYangName(),
attributeValue);
}
}
}
- private static int getChildNodeSizeWithoutUses(ContainerSchemaNode csn) {
+ private static int getChildNodeSizeWithoutUses(DataNodeContainer csn) {
int result = 0;
for (DataSchemaNode dsn : csn.getChildNodes()) {
- if (dsn.isAddedByUses() == false)
+ if (dsn.isAddedByUses() == false) {
result++;
+ }
}
return result;
}
private static AttributeIfc getAttributeValue(DataSchemaNode attrNode,
Module currentModule,
Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
- TypeProviderWrapper typeProviderWrapper, SchemaContext schemaContext) {
+ TypeProviderWrapper typeProviderWrapper, SchemaContext schemaContext, String packageName) {
if (attrNode instanceof LeafSchemaNode) {
// simple type
} else if (attrNode instanceof ContainerSchemaNode) {
// reference or TO
ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode;
- if (containerSchemaNode.getUses().size() == 1
- && getChildNodeSizeWithoutUses(containerSchemaNode) == 0) {
- // reference
- UsesNode usesNode = containerSchemaNode.getUses().iterator()
- .next();
- checkState(usesNode.getRefines().size() == 1,
- "Unexpected 'refine' child node size of "
- + containerSchemaNode);
- LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines()
- .values().iterator().next();
- checkState(refine.getUnknownSchemaNodes().size() == 1,
- "Unexpected unknown schema node size of " + refine);
- UnknownSchemaNode requiredIdentity = refine
- .getUnknownSchemaNodes().iterator().next();
- checkState(
- ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity
- .getNodeType()),
- "Unexpected language extension " + requiredIdentity);
- String prefixAndIdentityLocalName = requiredIdentity
- .getNodeParameter();
- // import should point to a module
- ServiceInterfaceEntry serviceInterfaceEntry = findSIE(
- prefixAndIdentityLocalName, currentModule,
- qNamesToSIEs, schemaContext);
- boolean mandatory = refine.getConstraints().isMandatory();
- return new DependencyAttribute(attrNode, serviceInterfaceEntry,
- mandatory, attrNode.getDescription());
+ Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(containerSchemaNode,
+ attrNode, currentModule, qNamesToSIEs, schemaContext);
+ if (dependencyAttributeOptional.isPresent()) {
+ return dependencyAttributeOptional.get();
} else {
- return TOAttribute.create(containerSchemaNode,
- typeProviderWrapper);
+ return TOAttribute.create(containerSchemaNode, typeProviderWrapper, packageName);
}
+
} else if (attrNode instanceof LeafListSchemaNode) {
return ListAttribute.create((LeafListSchemaNode) attrNode,
typeProviderWrapper);
} else if (attrNode instanceof ListSchemaNode) {
- return ListAttribute.create((ListSchemaNode) attrNode,
- typeProviderWrapper);
+ ListSchemaNode listSchemaNode = (ListSchemaNode) attrNode;
+ Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(listSchemaNode,
+ attrNode, currentModule, qNamesToSIEs, schemaContext);
+ if (dependencyAttributeOptional.isPresent()) {
+ return dependencyAttributeOptional.get();
+ } else {
+ return ListAttribute.create(listSchemaNode, typeProviderWrapper, packageName);
+ }
} else {
throw new UnsupportedOperationException(
"Unknown configuration node " + attrNode.toString());
}
}
+ private static Optional<? extends AbstractDependencyAttribute> extractDependency(DataNodeContainer dataNodeContainer,
+ DataSchemaNode attrNode,
+ Module currentModule,
+ Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
+ SchemaContext schemaContext) {
+ if (dataNodeContainer.getUses().size() == 1
+ && getChildNodeSizeWithoutUses(dataNodeContainer) == 0) {
+ // reference
+ UsesNode usesNode = dataNodeContainer.getUses().iterator()
+ .next();
+ checkState(usesNode.getRefines().size() == 1,
+ "Unexpected 'refine' child node size of "
+ + dataNodeContainer);
+ LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines()
+ .values().iterator().next();
+ checkState(refine.getUnknownSchemaNodes().size() == 1,
+ "Unexpected unknown schema node size of " + refine);
+ UnknownSchemaNode requiredIdentity = refine
+ .getUnknownSchemaNodes().iterator().next();
+ checkState(
+ ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity
+ .getNodeType()),
+ "Unexpected language extension " + requiredIdentity);
+ String prefixAndIdentityLocalName = requiredIdentity
+ .getNodeParameter();
+ // import should point to a module
+ ServiceInterfaceEntry serviceInterfaceEntry = findSIE(
+ prefixAndIdentityLocalName, currentModule,
+ qNamesToSIEs, schemaContext);
+ boolean mandatory = refine.getConstraints().isMandatory();
+ AbstractDependencyAttribute reference;
+ if (dataNodeContainer instanceof ContainerSchemaNode ){
+ reference = new DependencyAttribute(attrNode, serviceInterfaceEntry,
+ mandatory, attrNode.getDescription());
+ } else {
+ reference = new ListDependenciesAttribute(attrNode, serviceInterfaceEntry,
+ mandatory, attrNode.getDescription());
+ }
+ return Optional.of(reference);
+ }
+ return Optional.absent();
+ }
+
private static ServiceInterfaceEntry findSIE(
String prefixAndIdentityLocalName, Module currentModule,
Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
private final Set<Rpc> rpcs;
@VisibleForTesting
- public RuntimeBeanEntry(String packageName,
+ RuntimeBeanEntry(String packageName,
DataSchemaNode nodeForReporting, String yangName,
String javaNamePrefix, boolean isRoot,
Optional<String> keyYangName, List<AttributeIfc> attributes,
ContainerSchemaNode container = (ContainerSchemaNode) child;
// this can be either TO or hierarchical RB
TOAttribute toAttribute = TOAttribute.create(container,
- typeProviderWrapper);
+ typeProviderWrapper, packageName);
attributes.add(toAttribute);
} else if (child instanceof ListSchemaNode) {
if (isInnerStateBean(child)) {
runtimeBeanEntries.add(hierarchicalChild);
} else /* ordinary list attribute */{
ListAttribute listAttribute = ListAttribute.create(
- (ListSchemaNode) child, typeProviderWrapper);
+ (ListSchemaNode) child, typeProviderWrapper, packageName);
attributes.add(listAttribute);
}
} else if (rpcDefinition.getOutput().getChildNodes().size() == 1) {
DataSchemaNode returnDSN = rpcDefinition.getOutput()
.getChildNodes().iterator().next();
- returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper);
+ returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper, packageName);
} else {
throw new IllegalArgumentException(
attributes, rpcs);
}
- private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper) {
+ private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper,
+ String packageName) {
if (child instanceof LeafSchemaNode) {
LeafSchemaNode leaf = (LeafSchemaNode) child;
return new JavaAttribute(leaf, typeProviderWrapper);
} else if (child instanceof ContainerSchemaNode) {
ContainerSchemaNode container = (ContainerSchemaNode) child;
- TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper);
+ TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper, packageName);
return toAttribute;
} else if (child instanceof ListSchemaNode) {
- return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper);
+ return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper, packageName);
} else if (child instanceof LeafListSchemaNode) {
return ListAttribute.create((LeafListSchemaNode) child, typeProviderWrapper);
} else {
--- /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.yangjmxgenerator.attribute;
+
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public abstract class AbstractDependencyAttribute extends AbstractAttribute implements TypedAttribute {
+
+ protected final Dependency dependency;
+ protected final String nullableDescription, nullableDefault;
+
+ public AbstractDependencyAttribute(DataSchemaNode attrNode,
+ ServiceInterfaceEntry sie, boolean mandatory,
+ String nullableDescription) {
+ super(attrNode);
+ dependency = new Dependency(sie, mandatory);
+ this.nullableDescription = nullableDescription;
+ nullableDefault = null;
+ }
+
+ public Dependency getDependency() {
+ return dependency;
+ }
+
+ @Override
+ public String getNullableDescription() {
+ return nullableDescription;
+ }
+
+ @Override
+ public String getNullableDefault() {
+ return nullableDefault;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ if (!super.equals(o))
+ return false;
+
+ AbstractDependencyAttribute that = (AbstractDependencyAttribute) o;
+
+ if (dependency != null ? !dependency.equals(that.dependency)
+ : that.dependency != null)
+ return false;
+ if (nullableDefault != null ? !nullableDefault
+ .equals(that.nullableDefault) : that.nullableDefault != null)
+ return false;
+ if (nullableDescription != null ? !nullableDescription
+ .equals(that.nullableDescription)
+ : that.nullableDescription != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (dependency != null ? dependency.hashCode() : 0);
+ result = 31
+ * result
+ + (nullableDescription != null ? nullableDescription.hashCode()
+ : 0);
+ result = 31 * result
+ + (nullableDefault != null ? nullableDefault.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "{" + getAttributeYangName() + ","
+ + "dependency=" + dependency + '}';
+ }
+
+
+}
--- /dev/null
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+
+public class Dependency {
+ private final ServiceInterfaceEntry sie;
+ private final boolean mandatory;
+
+ public Dependency(ServiceInterfaceEntry sie, boolean mandatory) {
+ this.sie = sie;
+ this.mandatory = mandatory;
+ }
+
+ public ServiceInterfaceEntry getSie() {
+ return sie;
+ }
+
+ public boolean isMandatory() {
+ return mandatory;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ Dependency that = (Dependency) o;
+
+ if (mandatory != that.mandatory)
+ return false;
+ if (!sie.equals(that.sie))
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = sie.hashCode();
+ result = 31 * result + (mandatory ? 1 : 0);
+ return result;
+ }
+ }
import javax.management.ObjectName;
import javax.management.openmbean.SimpleType;
-public class DependencyAttribute extends AbstractAttribute implements
- TypedAttribute {
+public class DependencyAttribute extends AbstractDependencyAttribute {
- private final Dependency dependency;
- private final String nullableDescription, nullableDefault;
public DependencyAttribute(DataSchemaNode attrNode,
ServiceInterfaceEntry sie, boolean mandatory,
String nullableDescription) {
- super(attrNode);
- dependency = new Dependency(sie, mandatory);
- this.nullableDescription = nullableDescription;
- nullableDefault = null;
+ super(attrNode, sie, mandatory, nullableDescription);
}
@Override
return Types.typeForClass(ObjectName.class);
}
- public Dependency getDependency() {
- return dependency;
- }
-
- @Override
- public String getNullableDescription() {
- return nullableDescription;
- }
-
- @Override
- public String getNullableDefault() {
- return nullableDefault;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
- if (!super.equals(o))
- return false;
-
- DependencyAttribute that = (DependencyAttribute) o;
-
- if (dependency != null ? !dependency.equals(that.dependency)
- : that.dependency != null)
- return false;
- if (nullableDefault != null ? !nullableDefault
- .equals(that.nullableDefault) : that.nullableDefault != null)
- return false;
- if (nullableDescription != null ? !nullableDescription
- .equals(that.nullableDescription)
- : that.nullableDescription != null)
- return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = super.hashCode();
- result = 31 * result + (dependency != null ? dependency.hashCode() : 0);
- result = 31
- * result
- + (nullableDescription != null ? nullableDescription.hashCode()
- : 0);
- result = 31 * result
- + (nullableDefault != null ? nullableDefault.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "DependencyAttribute{" + getAttributeYangName() + ","
- + "dependency=" + dependency + '}';
- }
-
@Override
public SimpleType<?> getOpenType() {
return SimpleType.OBJECTNAME;
}
- public static class Dependency {
- private final ServiceInterfaceEntry sie;
- private final boolean mandatory;
-
- public Dependency(ServiceInterfaceEntry sie, boolean mandatory) {
- this.sie = sie;
- this.mandatory = mandatory;
- }
-
- public ServiceInterfaceEntry getSie() {
- return sie;
- }
-
- public boolean isMandatory() {
- return mandatory;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- Dependency that = (Dependency) o;
-
- if (mandatory != that.mandatory)
- return false;
- if (!sie.equals(that.sie))
- return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = sie.hashCode();
- result = 31 * result + (mandatory ? 1 : 0);
- return result;
- }
- }
-
}
*/
package org.opendaylight.controller.config.yangjmxgenerator.attribute;
-import javax.management.openmbean.ArrayType;
-import javax.management.openmbean.OpenDataException;
-import javax.management.openmbean.OpenType;
-
import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
+import org.opendaylight.yangtools.binding.generator.util.Types;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-public class ListAttribute extends AbstractAttribute {
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import java.util.List;
+
+public class ListAttribute extends AbstractAttribute implements TypedAttribute {
private final String nullableDescription, nullableDefault;
- private final AttributeIfc innerAttribute;
+ private final TypedAttribute innerAttribute;
public static ListAttribute create(ListSchemaNode node,
- TypeProviderWrapper typeProvider) {
+ TypeProviderWrapper typeProvider, String packageName) {
- AttributeIfc innerAttribute = TOAttribute.create(node, typeProvider);
+ TOAttribute innerAttribute = TOAttribute.create(node, typeProvider, packageName);
return new ListAttribute(node, innerAttribute, node.getDescription());
}
public static ListAttribute create(LeafListSchemaNode node,
TypeProviderWrapper typeProvider) {
- AttributeIfc innerAttribute = new JavaAttribute(node, typeProvider);
+ JavaAttribute innerAttribute = new JavaAttribute(node, typeProvider);
return new ListAttribute(node, innerAttribute, node.getDescription());
}
- ListAttribute(DataSchemaNode attrNode, AttributeIfc innerAttribute,
+ ListAttribute(DataSchemaNode attrNode, TypedAttribute innerAttribute,
String description) {
super(attrNode);
this.nullableDescription = description;
return true;
}
+
+ @Override
+ public Type getType() {
+ return Types.parameterizedTypeFor(Types.typeForClass(List.class), innerAttribute.getType());
+ }
+
@Override
public ArrayType<?> getOpenType() {
- OpenType<?> inerOpenType = innerAttribute.getOpenType();
+ OpenType<?> innerOpenType = innerAttribute.getOpenType();
+ return constructArrayType(innerOpenType);
+ }
+
+ static ArrayType<?> constructArrayType(OpenType<?> innerOpenType){
try {
- return new ArrayType<>(1, inerOpenType);
+ return new ArrayType<>(1, innerOpenType);
} catch (OpenDataException e) {
throw new RuntimeException("Unable to create " + ArrayType.class
- + " with inner element of type " + inerOpenType, e);
+ + " with inner element of type " + innerOpenType, e);
}
}
--- /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.yangjmxgenerator.attribute;
+
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+import org.opendaylight.yangtools.binding.generator.util.Types;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import java.util.List;
+
+public class ListDependenciesAttribute extends AbstractDependencyAttribute {
+
+ public ListDependenciesAttribute(DataSchemaNode attrNode, ServiceInterfaceEntry sie, boolean mandatory, String nullableDescription) {
+ super(attrNode, sie, mandatory, nullableDescription);
+ }
+
+ @Override
+ public Type getType() {
+ return Types.parameterizedTypeFor(Types.typeForClass(List.class), Types.typeForClass(ObjectName.class));
+ }
+
+ @Override
+ public ArrayType<?> getOpenType() {
+ OpenType<?> innerOpenType = SimpleType.OBJECTNAME;
+ return ListAttribute.constructArrayType(innerOpenType);
+ }
+
+}
import com.google.common.collect.Sets;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
+import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import java.util.Map.Entry;
import java.util.Set;
-public class TOAttribute extends AbstractAttribute {
+public class TOAttribute extends AbstractAttribute implements TypedAttribute {
private final String nullableDescription, nullableDefault;
private final Map<String, AttributeIfc> yangNameToAttributeMap;
private final Map<String, String> attributeNameMap;
+ private final String packageName;
private static final Set<Class<? extends DataSchemaNode>> ALLOWED_CHILDREN = Sets
.newHashSet();
}
public static <T extends DataNodeContainer & AugmentationTarget & DataSchemaNode> TOAttribute create(
- T containerSchemaNode, TypeProviderWrapper typeProviderWrapper) {
+ T containerSchemaNode, TypeProviderWrapper typeProviderWrapper, String packageName) {
// Transfer Object: get the leaves
Map<String, AttributeIfc> map = new HashMap<>();
Map<String, String> attributeNameMap = new HashMap<>();
String yangName = dataSchemaNode.getQName().getLocalName();
map.put(yangName,
createInnerAttribute(dataSchemaNode,
- typeProviderWrapper));
+ typeProviderWrapper, packageName));
} catch (IllegalArgumentException e) {
throw new IllegalStateException("Unable to create TO", e);
}
}
return new TOAttribute(containerSchemaNode, map, attributeNameMap,
- containerSchemaNode.getDescription());
+ containerSchemaNode.getDescription(), packageName);
}
private static AttributeIfc createInnerAttribute(
DataSchemaNode dataSchemaNode,
- TypeProviderWrapper typeProviderWrapper) {
+ TypeProviderWrapper typeProviderWrapper, String packageName) {
Class<? extends DataSchemaNode> type = isAllowedType(dataSchemaNode);
if (type.equals(LeafSchemaNode.class))
typeProviderWrapper);
else if (type.equals(ListSchemaNode.class))
return ListAttribute.create((ListSchemaNode) dataSchemaNode,
- typeProviderWrapper);
+ typeProviderWrapper, packageName);
else if (type.equals(LeafListSchemaNode.class))
return ListAttribute.create((LeafListSchemaNode) dataSchemaNode,
typeProviderWrapper);
else if (type.equals(ContainerSchemaNode.class))
return TOAttribute.create((ContainerSchemaNode) dataSchemaNode,
- typeProviderWrapper);
+ typeProviderWrapper, packageName);
throw new IllegalStateException("This should never happen");
}
private TOAttribute(DataSchemaNode attrNode,
Map<String, AttributeIfc> transferObject,
- Map<String, String> attributeNameMap, String nullableDescription) {
+ Map<String, String> attributeNameMap, String nullableDescription, String packageName) {
super(attrNode);
yangNameToAttributeMap = transferObject;
this.attributeNameMap = attributeNameMap;
this.nullableDescription = nullableDescription;
nullableDefault = null;
+ this.packageName = packageName;
}
public Map<String, String> getAttributeNameMap() {
+ yangNameToAttributeMap + '}';
}
+ @Override
+ public Type getType() {
+ // TODO: ReferencedTypeImpl from Types
+ return new ReferencedTypeImpl(packageName, getUpperCaseCammelCase());
+ }
+
@Override
public CompositeType getOpenType() {
String description = getNullableDescription() == null ? getAttributeYangName()
}
}
+ public String getPackageName() {
+ return packageName;
+ }
+
private static final class FunctionImpl implements
Function<Entry<String, AttributeIfc>, OpenType<?>> {
private final String[] itemNames;
*/
package org.opendaylight.controller.config.yangjmxgenerator;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-
-import javax.management.openmbean.ArrayType;
-import javax.management.openmbean.CompositeType;
-import javax.management.openmbean.SimpleType;
-
+import com.google.common.collect.Sets;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute;
import org.opendaylight.yangtools.binding.generator.util.Types;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
-import com.google.common.collect.Sets;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.SimpleType;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
public class ModuleMXBeanEntryTest extends AbstractYangTest {
public static final String EVENTBUS_MXB_NAME = "eventbus";
public static final String ASYNC_EVENTBUS_MXB_NAME = "async-eventbus";
public static final String THREADFACTORY_NAMING_MXB_NAME = "threadfactory-naming";
public static final String THREADPOOL_DYNAMIC_MXB_NAME = "threadpool-dynamic";
+ public static final String THREADPOOL_REGISTRY_IMPL_NAME = "threadpool-registry-impl";
public static final String BGP_LISTENER_IMPL_MXB_NAME = "bgp-listener-impl";
assertNotNull(namesToMBEs);
Set<String> expectedMXBeanNames = Sets.newHashSet(EVENTBUS_MXB_NAME,
ASYNC_EVENTBUS_MXB_NAME, THREADFACTORY_NAMING_MXB_NAME,
- THREADPOOL_DYNAMIC_MXB_NAME);
+ THREADPOOL_DYNAMIC_MXB_NAME, THREADPOOL_REGISTRY_IMPL_NAME);
assertThat(namesToMBEs.keySet(), is(expectedMXBeanNames));
return namesToMBEs;
}
}
}
+ { // test multiple dependencies
+ ModuleMXBeanEntry threadPoolRegistry = namesToMBEs.get(THREADPOOL_REGISTRY_IMPL_NAME);
+ Map<String, AttributeIfc> attributes = threadPoolRegistry.getAttributes();
+ assertEquals(1, attributes.size());
+ AttributeIfc threadpoolsAttr = attributes.get("threadpools");
+ assertNotNull(threadpoolsAttr);
+ assertTrue(threadpoolsAttr instanceof ListDependenciesAttribute);
+ ListDependenciesAttribute threadpools = (ListDependenciesAttribute) threadpoolsAttr;
+ }
}
}
@Test
public void testReadingIdentities_threadsJavaModule() {
- Map<String /* identity name */, Optional<QName>> expectedIdentitiesToBases = ImmutableMap
- .of("eventbus", Optional.of(MODULE_TYPE_Q_NAME), "async-eventbus", Optional.of(MODULE_TYPE_Q_NAME),
- "threadfactory-naming", Optional.of(MODULE_TYPE_Q_NAME), "threadpool-dynamic",
- Optional.of(MODULE_TYPE_Q_NAME), "thread-rpc-context", Optional.<QName>absent());
+ Map<String /* identity name */, Optional<QName>> expectedIdentitiesToBases = new HashMap(){{
+ put(ModuleMXBeanEntryTest.EVENTBUS_MXB_NAME, Optional.of(MODULE_TYPE_Q_NAME));
+ put(ModuleMXBeanEntryTest.ASYNC_EVENTBUS_MXB_NAME, Optional.of(MODULE_TYPE_Q_NAME));
+ put(ModuleMXBeanEntryTest.THREADFACTORY_NAMING_MXB_NAME, Optional.of(MODULE_TYPE_Q_NAME));
+ put(ModuleMXBeanEntryTest.THREADPOOL_DYNAMIC_MXB_NAME, Optional.of(MODULE_TYPE_Q_NAME));
+ put("thread-rpc-context", Optional.absent());
+ put(ModuleMXBeanEntryTest.THREADPOOL_REGISTRY_IMPL_NAME, Optional.of(MODULE_TYPE_Q_NAME));
+ }};
+
assertAllIdentitiesAreExpected(threadsJavaModule,
expectedIdentitiesToBases);
}
}
}
}
+
+ identity threadpool-registry-impl {
+ base config:module-type;
+ config:java-name-prefix ThreadPoolRegistryImpl;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case threadpool-registry-impl {
+ when "/config:modules/config:module/config:type = 'threadpool-registry-impl'";
+
+ // list of dependencies:
+ list threadpools {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity th2:threadpool;
+ }
+ }
+ }
+ }
+ }
}
}
}
}
+
+ list testing-deps {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity test:testing;
+ }
+ }
+ }
}
}
<artifactId>config-netconf-connector</artifactId>
<version>${netconf.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-impl</artifactId>
sstr = Pattern.compile("OUTPUT=(.*)").matcher(actiongrp);
if (sstr.matches()) {
for (String t : sstr.group(1).split(",")) {
- Matcher n = Pattern.compile("(?:(\\d+))").matcher(t);
- if (n.matches()) {
- String port = n.group(1);
- if (port != null) {
- if (!isPortValid(sw, port)) {
- String msg = String.format("Output port %s is not valid for this switch", port);
- if (!containerName.equals(GlobalConstants.DEFAULT.toString())) {
- msg += " in Container " + containerName;
- }
- return new Status(StatusCode.BADREQUEST, msg);
+ if (t != null) {
+ if (!isPortValid(sw, t)) {
+ String msg = String.format("Output port %s is not valid for this switch", t);
+ if (!containerName.equals(GlobalConstants.DEFAULT.toString())) {
+ msg += " in Container " + containerName;
}
+ return new Status(StatusCode.BADREQUEST, msg);
}
- } else {
- String msg = String.format("Output port %s is not valid", t);
- return new Status(StatusCode.BADREQUEST, msg);
}
}
continue;
sstr = Pattern.compile("ENQUEUE=(.*)").matcher(actiongrp);
if (sstr.matches()) {
for (String t : sstr.group(1).split(",")) {
- Matcher n = Pattern.compile("(?:(\\d+:\\d+))").matcher(t);
- if (n.matches()) {
- if (n.group(1) != null) {
- String port = n.group(1).split(":")[0];
- if (!isPortValid(sw, port)) {
- String msg = String.format("Output port %d is not valid for this switch", port);
- if (!containerName.equals(GlobalConstants.DEFAULT.toString())) {
- msg += " in Container " + containerName;
- }
- return new Status(StatusCode.BADREQUEST, msg);
+ if (t != null) {
+ String port = t.split(":")[0];
+ if (!isPortValid(sw, port)) {
+ String msg = String.format("Output port %d is not valid for this switch", port);
+ if (!containerName.equals(GlobalConstants.DEFAULT.toString())) {
+ msg += " in Container " + containerName;
}
+ return new Status(StatusCode.BADREQUEST, msg);
}
- } else {
- String msg = String.format("Enqueue port %s is not valid", t);
- return new Status(StatusCode.BADREQUEST, msg);
}
}
continue;
sstr = Pattern.compile(ActionType.OUTPUT + "=(.*)").matcher(actiongrp);
if (sstr.matches()) {
for (String t : sstr.group(1).split(",")) {
- Matcher n = Pattern.compile("(?:(\\d+))").matcher(t);
- if (n.matches()) {
- if (n.group(1) != null) {
- String nc = String.format("%s|%s@%s", node.getType(), n.group(1), node.toString());
- actionList.add(new Output(NodeConnector.fromString(nc)));
- }
+ if (t != null) {
+ String nc = String.format("%s|%s@%s", node.getType(), t, node.toString());
+ actionList.add(new Output(NodeConnector.fromString(nc)));
}
}
continue;
sstr = Pattern.compile(ActionType.ENQUEUE + "=(.*)").matcher(actiongrp);
if (sstr.matches()) {
for (String t : sstr.group(1).split(",")) {
- Matcher n = Pattern.compile("(?:(\\d+:\\d+))").matcher(t);
- if (n.matches()) {
- if (n.group(1) != null) {
- String parts[] = n.group(1).split(":");
- String nc = String.format("%s|%s@%s", node.getType(), parts[0], node.toString());
- if (parts.length == 1) {
- actionList.add(new Enqueue(NodeConnector.fromString(nc)));
- } else {
- actionList
- .add(new Enqueue(NodeConnector.fromString(nc), Integer.parseInt(parts[1])));
- }
+ if (t != null) {
+ String parts[] = t.split(":");
+ String nc = String.format("%s|%s@%s", node.getType(), parts[0], node.toString());
+ if (parts.length == 1) {
+ actionList.add(new Enqueue(NodeConnector.fromString(nc)));
+ } else {
+ actionList
+ .add(new Enqueue(NodeConnector.fromString(nc), Integer.parseInt(parts[1])));
}
}
}
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupListener;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.OriginalGroupBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.UpdatedGroupBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
import org.slf4j.LoggerFactory;
@SuppressWarnings("unused")
-public class GroupConsumerImpl implements IForwardingRulesManager {
+public class GroupConsumerImpl {
protected static final Logger logger = LoggerFactory.getLogger(GroupConsumerImpl.class);
private final GroupEventListener groupEventListener = new GroupEventListener();
private SalGroupService groupService;
private GroupDataCommitHandler groupCommitHandler;
- private ConcurrentMap<GroupKey, Group> originalSwGroupView;
- private ConcurrentMap<GroupKey, Group> installedSwGroupView;
-
- private ConcurrentMap<Node, List<Group>> nodeGroups;
- private ConcurrentMap<GroupKey, Group> inactiveGroups;
-
- private IClusterContainerServices clusterGroupContainerService = null;
private IContainer container;
public GroupConsumerImpl() {
InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Groups.class).toInstance();
- groupService = FRMConsumerImpl.getProviderSession().getRpcService(SalGroupService.class);
-
- if (!(cacheStartup())) {
- logger.error("Unanle to allocate/retrieve group cache");
- System.out.println("Unable to allocate/retrieve group cache");
- }
+ groupService = FRMConsumerImpl.getProviderSession().getRpcService(SalGroupService.class);
if (null == groupService) {
logger.error("Consumer SAL Group Service is down or NULL. FRM may not function as intended");
groupCommitHandler = new GroupDataCommitHandler();
FRMConsumerImpl.getDataProviderService().registerCommitHandler(path, groupCommitHandler);
- clusterGroupContainerService = FRMConsumerImpl.getClusterContainerService();
- container = FRMConsumerImpl.getContainer();
- }
-
- private boolean allocateGroupCaches() {
- if (this.clusterGroupContainerService == null) {
- logger.warn("Group: Un-initialized clusterGroupContainerService, can't create cache");
- return false;
- }
-
- try {
- clusterGroupContainerService.createCache("frm.originalSwGroupView",
- EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
-
- clusterGroupContainerService.createCache("frm.installedSwGroupView",
- EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
-
- clusterGroupContainerService.createCache("frm.inactiveGroups",
- EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
-
- clusterGroupContainerService.createCache("frm.nodeGroups",
- EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
-
- // TODO for cluster mode
- /*
- * clusterGroupContainerService.createCache(WORK_STATUS_CACHE,
- * EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL,
- * IClusterServices.cacheMode.ASYNC));
- *
- * clusterGroupContainerService.createCache(WORK_ORDER_CACHE,
- * EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL,
- * IClusterServices.cacheMode.ASYNC));
- */
-
- } catch (CacheConfigException cce) {
- logger.error("Group CacheConfigException");
- return false;
-
- } catch (CacheExistException cce) {
- logger.error(" Group CacheExistException");
- }
-
- return true;
- }
-
- private void nonClusterGroupObjectCreate() {
- originalSwGroupView = new ConcurrentHashMap<GroupKey, Group>();
- installedSwGroupView = new ConcurrentHashMap<GroupKey, Group>();
- nodeGroups = new ConcurrentHashMap<Node, List<Group>>();
- inactiveGroups = new ConcurrentHashMap<GroupKey, Group>();
- }
-
- @SuppressWarnings({ "unchecked" })
- private boolean retrieveGroupCaches() {
- ConcurrentMap<?, ?> map;
-
- if (this.clusterGroupContainerService == null) {
- logger.warn("Group: un-initialized clusterGroupContainerService, can't retrieve cache");
- nonClusterGroupObjectCreate();
- return false;
- }
+ }
- map = clusterGroupContainerService.getCache("frm.originalSwGroupView");
- if (map != null) {
- originalSwGroupView = (ConcurrentMap<GroupKey, Group>) map;
- } else {
- logger.error("Retrieval of cache(originalSwGroupView) failed");
- return false;
- }
-
- map = clusterGroupContainerService.getCache("frm.installedSwGroupView");
- if (map != null) {
- installedSwGroupView = (ConcurrentMap<GroupKey, Group>) map;
- } else {
- logger.error("Retrieval of cache(installedSwGroupView) failed");
- return false;
- }
-
- map = clusterGroupContainerService.getCache("frm.inactiveGroups");
- if (map != null) {
- inactiveGroups = (ConcurrentMap<GroupKey, Group>) map;
- } else {
- logger.error("Retrieval of cache(inactiveGroups) failed");
- return false;
- }
-
- map = clusterGroupContainerService.getCache("frm.nodeGroups");
- if (map != null) {
- nodeGroups = (ConcurrentMap<Node, List<Group>>) map;
- } else {
- logger.error("Retrieval of cache(nodeGroup) failed");
- return false;
- }
-
- return true;
- }
-
- private boolean cacheStartup() {
- if (allocateGroupCaches()) {
- if (retrieveGroupCaches()) {
- return true;
- }
- }
-
- return false;
- }
-
- public Status validateGroup(Group group, FRMUtil.operation operation) {
- String containerName;
+ public Status validateGroup(Group group) {
String groupName;
Iterator<Bucket> bucketIterator;
boolean returnResult;
Buckets groupBuckets;
- if (null != group) {
- containerName = group.getContainerName();
-
- if (null == containerName) {
- containerName = GlobalConstants.DEFAULT.toString();
- } else if (!FRMUtil.isNameValid(containerName)) {
- logger.error("Container Name is invalid %s" + containerName);
- return new Status(StatusCode.BADREQUEST, "Container Name is invalid");
- }
-
+ if (null != group) {
groupName = group.getGroupName();
if (!FRMUtil.isNameValid(groupName)) {
logger.error("Group Name is invalid %s" + groupName);
* @param path
* @param dataObject
*/
- private Status updateGroup(InstanceIdentifier<?> path, Group groupUpdateDataObject) {
- GroupKey groupKey = groupUpdateDataObject.getKey();
+ private void updateGroup(InstanceIdentifier<?> path,
+ Group originalGroupDataObject, Group updatedGroupDataObject) {
+
+ GroupKey groupKey = updatedGroupDataObject.getKey();
+ // Node nodeInstanceID = path.firstIdentifierOf("Node");
UpdatedGroupBuilder updateGroupBuilder = null;
-
- Status groupOperationStatus = validateGroup(groupUpdateDataObject, FRMUtil.operation.UPDATE);
+ Status groupOperationStatus = validateGroup(updatedGroupDataObject);
if (!groupOperationStatus.isSuccess()) {
- logger.error("Group data object validation failed %s" + groupUpdateDataObject.getGroupName());
- return groupOperationStatus;
+ logger.error("Group data object validation failed %s" + updatedGroupDataObject.getGroupName());
+ return;
}
- UpdateGroupInputBuilder groupData = new UpdateGroupInputBuilder();
- updateGroupBuilder = new UpdatedGroupBuilder();
- updateGroupBuilder.setGroupId(new GroupId(groupUpdateDataObject.getId()));
- updateGroupBuilder.fieldsFrom(groupUpdateDataObject);
- groupData.setUpdatedGroup(updateGroupBuilder.build());
- groupService.updateGroup(groupData.build());
- return groupOperationStatus;
+ UpdateGroupInputBuilder groupInputBuilder = new UpdateGroupInputBuilder();
+ groupInputBuilder.setNode(updatedGroupDataObject.getNode());
+ updateGroupBuilder = new UpdatedGroupBuilder(updatedGroupDataObject);
+ updateGroupBuilder.setGroupId(new GroupId(updatedGroupDataObject.getId()));
+ groupInputBuilder.setUpdatedGroup(updateGroupBuilder.build());
+ OriginalGroupBuilder originalGroupBuilder = new OriginalGroupBuilder(originalGroupDataObject);
+ groupInputBuilder.setOriginalGroup(originalGroupBuilder.build());
+ groupService.updateGroup(groupInputBuilder.build());
+ return;
}
/**
* @param path
* @param dataObject
*/
- private Status addGroup(InstanceIdentifier<?> path, Group groupAddDataObject) {
+ private void addGroup(InstanceIdentifier<?> path, Group groupAddDataObject) {
GroupKey groupKey = groupAddDataObject.getKey();
- Status groupOperationStatus = validateGroup(groupAddDataObject, FRMUtil.operation.ADD);
+ Status groupOperationStatus = validateGroup(groupAddDataObject);
if (!groupOperationStatus.isSuccess()) {
logger.error("Group data object validation failed %s" + groupAddDataObject.getGroupName());
- return groupOperationStatus;
+ return;
}
AddGroupInputBuilder groupData = new AddGroupInputBuilder();
groupData.setGroupType(groupAddDataObject.getGroupType());
groupData.setNode(groupAddDataObject.getNode());
groupService.addGroup(groupData.build());
- return groupOperationStatus;
+ return;
}
/**
* @param path
* @param dataObject
*/
- private Status removeGroup(InstanceIdentifier<?> path, Group groupRemoveDataObject) {
+ private void removeGroup(InstanceIdentifier<?> path, Group groupRemoveDataObject) {
GroupKey groupKey = groupRemoveDataObject.getKey();
- Status groupOperationStatus = validateGroup(groupRemoveDataObject, FRMUtil.operation.ADD);
+ Status groupOperationStatus = validateGroup(groupRemoveDataObject);
if (!groupOperationStatus.isSuccess()) {
logger.error("Group data object validation failed %s" + groupRemoveDataObject.getGroupName());
- return groupOperationStatus;
+ return;
}
RemoveGroupInputBuilder groupData = new RemoveGroupInputBuilder();
groupData.setGroupType(groupRemoveDataObject.getGroupType());
groupData.setNode(groupRemoveDataObject.getNode());
groupService.removeGroup(groupData.build());
- return groupOperationStatus;
+ return;
}
private RpcResult<Void> commitToPlugin(InternalTransaction transaction) {
- for (Entry<InstanceIdentifier<?>, Group> entry : transaction.additions.entrySet()) {
-
- if (!addGroup(entry.getKey(), entry.getValue()).isSuccess()) {
- transaction.additions.remove(entry.getKey());
- return Rpcs.getRpcResult(false, null, Collections.<RpcError>emptySet());
- }
- }
-
- for (Entry<InstanceIdentifier<?>, Group> entry : transaction.updates.entrySet()) {
-
- if (!addGroup(entry.getKey(), entry.getValue()).isSuccess()) {
- transaction.updates.remove(entry.getKey());
- return Rpcs.getRpcResult(false, null, Collections.<RpcError>emptySet());
- }
- }
+ DataModification<InstanceIdentifier<?>, DataObject> modification = transaction.modification;
+ //get created entries
+ Set<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> createdEntries =
+ modification.getCreatedConfigurationData().entrySet();
+
+ //get updated entries
+ Set<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> updatedEntries =
+ new HashSet<Entry<InstanceIdentifier<? extends DataObject>, DataObject>>();
+
+ updatedEntries.addAll(modification.getUpdatedConfigurationData().entrySet());
+ updatedEntries.removeAll(createdEntries);
- for (InstanceIdentifier<?> groupId : transaction.removals) {
- DataObject removeValue = transaction.getModification().getOriginalConfigurationData().get(groupId);
-
- if(removeValue instanceof Group) {
- if(!removeGroup(groupId, (Group)removeValue).isSuccess()) {
- return Rpcs.getRpcResult(false, null, Collections.<RpcError>emptySet());
- }
- }
+ //get removed entries
+ Set<InstanceIdentifier<? extends DataObject>> removeEntriesInstanceIdentifiers =
+ modification.getRemovedConfigurationData();
+
+ for (Entry<InstanceIdentifier<? extends DataObject >, DataObject> entry : createdEntries) {
+ if(entry.getValue() instanceof Group) {
+ addGroup(entry.getKey(), (Group)entry.getValue());
+ }
+ }
+
+ for (Entry<InstanceIdentifier<?>, DataObject> entry : updatedEntries) {
+ if(entry.getValue() instanceof Group) {
+ Group originalGroup = (Group) modification.getOriginalConfigurationData().get(entry.getKey());
+ Group updatedGroup = (Group) entry.getValue();
+ updateGroup(entry.getKey(), originalGroup, updatedGroup);
+ }
+ }
+
+ for (InstanceIdentifier<?> instanceId : removeEntriesInstanceIdentifiers ) {
+ DataObject removeValue = modification.getOriginalConfigurationData().get(instanceId);
+ if(removeValue instanceof Group) {
+ removeGroup(instanceId, (Group)removeValue);
+ }
}
return Rpcs.getRpcResult(true, null, Collections.<RpcError>emptySet());
@Override
public DataCommitTransaction<InstanceIdentifier<?>, DataObject> requestCommit(
- DataModification<InstanceIdentifier<?>, DataObject> modification) {
- // We should verify transaction
- System.out.println("Coming in GroupDatacommitHandler");
+ DataModification<InstanceIdentifier<?>, DataObject> modification) {
InternalTransaction transaction = new InternalTransaction(modification);
transaction.prepareUpdate();
return transaction;
}
}
- private final class InternalTransaction implements DataCommitTransaction<InstanceIdentifier<?>, DataObject> {
+ private final class InternalTransaction implements DataCommitTransaction<InstanceIdentifier<?>, DataObject> {
private final DataModification<InstanceIdentifier<?>, DataObject> modification;
-
- @Override
- public DataModification<InstanceIdentifier<?>, DataObject> getModification() {
- return modification;
- }
-
- public InternalTransaction(DataModification<InstanceIdentifier<?>, DataObject> modification) {
+
+ public InternalTransaction(DataModification<InstanceIdentifier<?>, DataObject> modification) {
this.modification = modification;
}
-
- Map<InstanceIdentifier<?>, Group> additions = new HashMap<>();
- Map<InstanceIdentifier<?>, Group> updates = new HashMap<>();
- Set<InstanceIdentifier<?>> removals = new HashSet<>();
-
+
/**
* We create a plan which flows will be added, which will be updated and
* which will be removed based on our internal state.
*/
void prepareUpdate() {
- Set<Entry<InstanceIdentifier<?>, DataObject>> groupAdded = modification.getCreatedConfigurationData().entrySet();
- for (Entry<InstanceIdentifier<?>, DataObject> entry : groupAdded) {
- if (entry.getValue() instanceof Group) {
- Group group = (Group) entry.getValue();
- additions.put(entry.getKey(), group);
- }
-
- }
-
- Set<Entry<InstanceIdentifier<?>, DataObject>> groupUpdate = modification.getUpdatedConfigurationData().entrySet();
- for (Entry<InstanceIdentifier<?>, DataObject> entry : groupUpdate) {
- if (entry.getValue() instanceof Group) {
- Group group = (Group) entry.getValue();
- ///will be fixed once getUpdatedConfigurationData returns only updated data not created data with it.
- if (additions.containsKey(entry.getKey())) {
- updates.put(entry.getKey(), group);
- }
- }
-
- }
-
- removals = modification.getRemovedConfigurationData();
}
/**
@Override
public RpcResult<Void> finish() throws IllegalStateException {
- RpcResult<Void> rpcStatus = commitToPlugin(this);
- // We return true if internal transaction is successful.
- // return Rpcs.getRpcResult(true, null, Collections.emptySet());
+ RpcResult<Void> rpcStatus = commitToPlugin(this);
return rpcStatus;
}
*
*/
@Override
- public RpcResult<Void> rollback() throws IllegalStateException {
- // NOOP - we did not modified any internal state during
- // requestCommit phase
- // return Rpcs.getRpcResult(true, null, Collections.emptySet());
+ public RpcResult<Void> rollback() throws IllegalStateException {
+
+ ///needs to be implemented as per gerrit 3314
return Rpcs.getRpcResult(true, null, Collections.<RpcError>emptySet());
+ }
+ @Override
+ public DataModification<InstanceIdentifier<?>, DataObject> getModification() {
+ return modification;
}
}
}
}
-
- @Override
- public List<DataObject> get() {
-
- List<DataObject> orderedList = new ArrayList<DataObject>();
- Collection<Group> groupList = originalSwGroupView.values();
- for (Iterator<Group> iterator = groupList.iterator(); iterator.hasNext();) {
- orderedList.add(iterator.next());
- }
- return orderedList;
- }
-
- @Override
- public DataObject getWithName(String name, Node n) {
-
- if (this instanceof GroupConsumerImpl) {
- Collection<Group> groupList = originalSwGroupView.values();
- for (Iterator<Group> iterator = groupList.iterator(); iterator.hasNext();) {
- Group group = iterator.next();
- if (group.getNode().equals(n) && group.getGroupName().equals(name)) {
-
- return group;
- }
- }
- }
- return null;
- }
-}
+ }
description "IP ECN (2 bits in ToS field).";
type uint8;
}
-
- leaf ip-proto {
- description "IP Proto (IPv4 or IPv6 Protocol Number).";
- type inet:ip-version;
- }
}
grouping "ipv4-match-fields" {
description "Link layer target address for neighbour discovery message";
type yang:mac-address;
}
-
- leaf ipv6-exthdr {
- description "IPv6 Extension Header field";
- type uint16;
+
+ container "ipv6-ext-header" {
+ leaf ipv6-exthdr {
+ description "IPv6 Extension Header field";
+ type uint16;
+ }
+
+ leaf ipv6-exthdr-mask {
+ type binary;
+ }
}
}
<artifactId>netconf-impl</artifactId>
<version>${netconf.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-client</artifactId>
mavenBundle(CONTROLLER, "logback-config").versionAsInProject(), //
mavenBundle(CONTROLLER, "config-persister-api").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-api").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "ietf-netconf-monitoring").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "netconf-monitoring").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-client").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-util").versionAsInProject(), //
import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import javax.management.openmbean.ArrayType;
} else if (attributeIfc instanceof DependencyAttribute) {
return caseDependencyAttribute(((DependencyAttribute) attributeIfc).getOpenType());
} else if (attributeIfc instanceof ListAttribute) {
- return caseListAttribute(((ListAttribute) attributeIfc).getOpenType());
+ return caseListAttribute((ArrayType<?>) attributeIfc.getOpenType());
+ } else if (attributeIfc instanceof ListDependenciesAttribute) {
+ return caseListDependeciesAttribute((ArrayType<?>) attributeIfc.getOpenType());
} else if (attributeIfc instanceof TOAttribute) {
return caseTOAttribute(((TOAttribute) attributeIfc).getOpenType());
}
throw getIllegalArgumentException(attributeIfc);
}
+
private IllegalArgumentException getIllegalArgumentException(AttributeIfc attributeIfc) {
return new IllegalArgumentException("Unknown attribute type " + attributeIfc.getClass() + ", " + attributeIfc
+ " with open type:" + attributeIfc.getOpenType());
protected abstract T caseListAttribute(ArrayType<?> openType);
+ protected abstract T caseListDependeciesAttribute(ArrayType<?> openType);
private static class UnknownOpenTypeException extends RuntimeException {
public UnknownOpenTypeException(String message) {
import com.google.common.collect.Maps;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
return new ArrayAttributeReadingStrategy(lastAttribute.getNullableDefault(), innerStrategy);
}
+ @Override
+ protected AttributeReadingStrategy caseListDependeciesAttribute(ArrayType<?> openType) {
+ Preconditions.checkState(lastAttribute instanceof ListDependenciesAttribute);
+ AttributeReadingStrategy innerStrategy = caseDependencyAttribute(SimpleType.OBJECTNAME);
+ return new ArrayAttributeReadingStrategy(lastAttribute.getNullableDefault(), innerStrategy);
+ }
+
}
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
prepareStrategy(((ListAttribute) lastAttribute).getInnerAttribute()));
}
+ @Override
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseListDependeciesAttribute(ArrayType<?> openType) {
+ Preconditions.checkState(lastAttribute instanceof ListDependenciesAttribute);
+ return new ArrayAttributeMappingStrategy(openType, caseDependencyAttribute(SimpleType.OBJECTNAME));
+ }
+
}
import com.google.common.collect.Maps;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
return new ArrayAttributeResolvingStrategy(prepareStrategy(innerAttribute), openType);
}
+ @Override
+ protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseListDependeciesAttribute(ArrayType<?> openType) {
+ Preconditions.checkState(lastAttribute instanceof ListDependenciesAttribute);
+ return new ArrayAttributeResolvingStrategy(caseDependencyAttribute(SimpleType.OBJECTNAME), openType);
+ }
+
}
import com.google.common.collect.Maps;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
import org.w3c.dom.Document;
return new ArrayAttributeWritingStrategy(innerStrategy);
}
+ @Override
+ protected AttributeWritingStrategy caseListDependeciesAttribute(ArrayType<?> openType) {
+ Preconditions.checkState(lastAttribute instanceof ListDependenciesAttribute);
+ AttributeWritingStrategy innerStrategy = caseDependencyAttribute(SimpleType.OBJECTNAME);
+ return new ArrayAttributeWritingStrategy(innerStrategy);
+ }
+
}
public static ServiceInstance fromString(String instanceId) {
instanceId = instanceId.trim();
Matcher matcher = p.matcher(instanceId);
+ if(matcher.matches() == false) {
+ matcher = pDeprecated.matcher(instanceId);
+ }
+
Preconditions.checkArgument(matcher.matches(), "Unexpected format for provider, expected " + p.toString()
- + " but was " + instanceId);
+ + " or " + pDeprecated.toString() + " but was " + instanceId);
+
String factoryName = matcher.group(1);
String instanceName = matcher.group(2);
return new ServiceInstance(factoryName, instanceName);
return instanceName;
}
- private static final String blueprint = "/" + XmlNetconfConstants.CONFIG_KEY + "/"
+ private static final String blueprint = "/"
+ XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "["
- + XmlNetconfConstants.NAME_KEY + "='%s']/" + XmlNetconfConstants.INSTANCE_KEY + "["
+ + XmlNetconfConstants.TYPE_KEY + "='%s']["
+ XmlNetconfConstants.NAME_KEY + "='%s']";
- private static final String blueprintR = "/" + XmlNetconfConstants.CONFIG_KEY + "/"
+ // TODO unify with xpath in RuntimeRpc
+
+ // Previous version of xpath, needs to be supported for backwards compatibility (persisted configs by config-persister)
+ private static final String blueprintRDeprecated = "/" + XmlNetconfConstants.CONFIG_KEY + "/"
+ XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\["
+ XmlNetconfConstants.NAME_KEY + "='%s'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\["
+ XmlNetconfConstants.NAME_KEY + "='%s'\\]";
+ private static final String blueprintR = "/"
+ + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\["
+ + XmlNetconfConstants.TYPE_KEY + "='%s'\\]\\["
+ + XmlNetconfConstants.NAME_KEY + "='%s'\\]";
+
+ private static final Pattern pDeprecated = Pattern.compile(String.format(blueprintRDeprecated, "(.+)", "(.+)"));
private static final Pattern p = Pattern.compile(String.format(blueprintR, "(.+)", "(.+)"));
@Override
public class Get extends AbstractConfigNetconfOperation {
- public static final String GET = "get";
-
private final YangStoreSnapshot yangStoreSnapshot;
private static final Logger logger = LoggerFactory.getLogger(Get.class);
}
private static void checkXml(XmlElement xml) {
- xml.checkName(GET);
+ xml.checkName(XmlNetconfConstants.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);
+ throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for " + XmlNetconfConstants.GET);
}
@Override
protected String getOperationName() {
- return GET;
+ return XmlNetconfConstants.GET;
}
@Override
final Element element = runtime.toXml(runtimeBeans, configBeans, document);
- logger.info("{} operation successful", GET);
+ logger.info("{} operation successful", XmlNetconfConstants.GET);
return element;
}
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
+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.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.yangtools.yang.model.api.Module;
-import com.google.common.base.Optional;
-import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Manages life cycle of {@link YangStoreSnapshot}.
capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
// [RFC6241] 8.5. Rollback-on-Error Capability
capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
- // [RFC6022] get-schema RPC. TODO: implement rest of the RFC
- 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) {
return capability;
}
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.absent();
+ }
+
@Override
public Optional<String> getModuleName() {
return Optional.absent();
public Optional<String> getCapabilitySchema() {
return Optional.absent();
}
+
+ @Override
+ public Optional<List<String>> getLocation() {
+ return Optional.absent();
+ }
}
private static class YangStoreCapability extends BasicCapability {
private final String content;
private final String revision;
private final String moduleName;
+ private final String moduleNamespace;
public YangStoreCapability(Map.Entry<Module, String> moduleAndContent) {
super(getAsString(moduleAndContent.getKey()));
this.content = moduleAndContent.getValue();
Module module = moduleAndContent.getKey();
this.moduleName = module.getName();
+ this.moduleNamespace = module.getNamespace().toString();
this.revision = Util.writeDate(module.getRevision());
}
return Optional.of(moduleName);
}
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(moduleNamespace);
+ }
+
@Override
public Optional<String> getRevision() {
return Optional.of(revision);
// check after edit
commit();
Element response = getConfigRunning();
+ System.err.println(XmlUtil.toString(response));
checkBinaryLeafEdited(response);
checkTypeConfigAttribute(response);
checkTypedefs(response);
+ checkTestingDeps(response);
checkEnum(response);
checkBigDecimal(response);
verifyNoMoreInteractions(netconfOperationRouter);
}
- private void checkBigDecimal(Element response) {
- int size = response.getElementsByTagName("sleep-factor").getLength();
- assertEquals(1, size);
- }
-
private void closeSession() throws NetconfDocumentedException, ParserConfigurationException, SAXException,
IOException {
DefaultCloseSession closeOp = new DefaultCloseSession(NETCONF_SESSION_ID);
fail("Enum attribute " + enumName + ":" + enumContent + " not present in " + XmlUtil.toString(response));
}
+ private void checkTestingDeps(Element response) {
+ int testingDepsSize = response.getElementsByTagName("testing-deps").getLength();
+ assertEquals(2, testingDepsSize);
+ }
+
+ private void checkBigDecimal(Element response) {
+ int size = response.getElementsByTagName("sleep-factor").getLength();
+ assertEquals(1, size);
+ }
+
+
private void checkTypeConfigAttribute(Element response) {
XmlElement modulesElement = XmlElement.fromDomElement(response).getOnlyChildElement("data")
package org.opendaylight.controller.netconf.persist.impl;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Sets;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetSocketAddress;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.regex.Pattern;
-import javax.annotation.concurrent.ThreadSafe;
-import javax.management.InstanceNotFoundException;
-import javax.management.MBeanServerConnection;
-import javax.management.Notification;
-import javax.management.NotificationListener;
-import javax.management.ObjectName;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
-import org.opendaylight.controller.config.api.ConflictingVersionException;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
import org.opendaylight.controller.netconf.api.jmx.DefaultCommitOperationMXBean;
import org.opendaylight.controller.netconf.api.jmx.NetconfJMXNotification;
import org.opendaylight.controller.netconf.client.NetconfClient;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
-import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
-import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
+
+import javax.annotation.concurrent.ThreadSafe;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanServerConnection;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.regex.Pattern;
/**
* Responsible for listening for notifications from netconf containing latest
public class ConfigPersisterNotificationHandler implements NotificationListener, Closeable {
private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
- private static final int NETCONF_SEND_ATTEMPT_MS_DELAY = 1000;
- private static final int NETCONF_SEND_ATTEMPTS = 20;
-
- private final InetSocketAddress address;
- private final EventLoopGroup nettyThreadgroup;
-
- private NetconfClientDispatcher netconfClientDispatcher;
- private NetconfClient netconfClient;
-
- private final Persister persister;
- private final MBeanServerConnection mbeanServer;
-
-
- private final ObjectName on = DefaultCommitOperationMXBean.objectName;
-
- public static final long DEFAULT_TIMEOUT = 120000L;// 120 seconds until netconf must be stable
- private final long timeout;
+ private final MBeanServerConnection mBeanServerConnection;
+ private final NetconfClient netconfClient;
+ private final PersisterAggregator persisterAggregator;
private final Pattern ignoredMissingCapabilityRegex;
- public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address,
- MBeanServerConnection mbeanServer, Pattern ignoredMissingCapabilityRegex) {
- this(persister, address, mbeanServer, DEFAULT_TIMEOUT, ignoredMissingCapabilityRegex);
-
- }
-
- public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address,
- MBeanServerConnection mbeanServer, long timeout, Pattern ignoredMissingCapabilityRegex) {
- this.persister = persister;
- this.address = address;
- this.mbeanServer = mbeanServer;
- this.timeout = timeout;
-
- this.nettyThreadgroup = new NioEventLoopGroup();
+ public ConfigPersisterNotificationHandler(MBeanServerConnection mBeanServerConnection, NetconfClient netconfClient,
+ PersisterAggregator persisterAggregator, Pattern ignoredMissingCapabilityRegex) {
+ this.mBeanServerConnection = mBeanServerConnection;
+ this.netconfClient = netconfClient;
+ this.persisterAggregator = persisterAggregator;
this.ignoredMissingCapabilityRegex = ignoredMissingCapabilityRegex;
}
- public void init() throws InterruptedException {
- Optional<ConfigSnapshotHolder> maybeConfig = loadLastConfig();
-
- if (maybeConfig.isPresent()) {
- logger.debug("Last config found {}", persister);
- ConflictingVersionException lastException = null;
- pushLastConfigWithRetries(maybeConfig, lastException);
-
- } else {
- // this ensures that netconf is initialized, this is first
- // connection
- // this means we can register as listener for commit
- registerToNetconf(Collections.<String>emptySet());
-
- logger.info("No last config provided by backend storage {}", persister);
- }
+ public void init() {
registerAsJMXListener();
}
- private void pushLastConfigWithRetries(Optional<ConfigSnapshotHolder> maybeConfig, ConflictingVersionException lastException) throws InterruptedException {
- int maxAttempts = 30;
- for(int i = 0 ; i < maxAttempts; i++) {
- registerToNetconf(maybeConfig.get().getCapabilities());
-
- final String configSnapshot = maybeConfig.get().getConfigSnapshot();
- logger.trace("Pushing following xml to netconf {}", configSnapshot);
- try {
- pushLastConfig(XmlUtil.readXmlToElement(configSnapshot));
- return;
- } catch(ConflictingVersionException e) {
- closeClientAndDispatcher(netconfClient, netconfClientDispatcher);
- lastException = e;
- Thread.sleep(1000);
- } catch (SAXException | IOException e) {
- throw new IllegalStateException("Unable to load last config", e);
- }
- }
- throw new IllegalStateException("Failed to push configuration, maximum attempt count has been reached: "
- + maxAttempts, lastException);
- }
-
- private synchronized long registerToNetconf(Set<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;
-
- long deadline = pollingStart + timeout;
- while (System.currentTimeMillis() < deadline) {
- attempt++;
- netconfClientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup);
- try {
- netconfClient = new NetconfClient(this.toString(), address, delay, netconfClientDispatcher);
- } catch (IllegalStateException e) {
- logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e);
- netconfClientDispatcher.close();
- Thread.sleep(delay);
- continue;
- }
- currentCapabilities = netconfClient.getCapabilities();
-
- if (isSubset(currentCapabilities, expectedCaps)) {
- logger.debug("Hello from netconf stable with {} capabilities", currentCapabilities);
- long currentSessionId = netconfClient.getSessionId();
- logger.info("Session id received from netconf server: {}", currentSessionId);
- return currentSessionId;
- }
-
-
-
- logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, currentCapabilities);
-
- closeClientAndDispatcher(netconfClient, netconfClientDispatcher);
-
- Thread.sleep(delay);
- }
- Set<String> allNotFound = new HashSet<>(expectedCaps);
- allNotFound.removeAll(currentCapabilities);
- logger.error("Netconf server did not provide required capabilities. Expected but not found: {}, all expected {}, current {}",
- allNotFound, expectedCaps ,currentCapabilities);
- throw new RuntimeException("Netconf server did not provide required capabilities. Expected but not found:" + allNotFound);
-
- }
-
- private static void closeClientAndDispatcher(Closeable client, Closeable dispatcher) {
- Exception fromClient = null;
- try {
- client.close();
- } catch (Exception e) {
- fromClient = e;
- } finally {
- try {
- dispatcher.close();
- } catch (Exception e) {
- if (fromClient != null) {
- e.addSuppressed(fromClient);
- }
-
- throw new RuntimeException("Error closing temporary client ", e);
- }
- }
- }
-
- private boolean isSubset(Set<String> currentCapabilities, Set<String> expectedCaps) {
- for (String exCap : expectedCaps) {
- if (currentCapabilities.contains(exCap) == false)
- return false;
- }
- return true;
- }
-
private void registerAsJMXListener() {
logger.trace("Called registerAsJMXListener");
try {
- mbeanServer.addNotificationListener(on, this, null, null);
+ mBeanServerConnection.addNotificationListener(DefaultCommitOperationMXBean.objectName, this, null, null);
} catch (InstanceNotFoundException | IOException e) {
throw new RuntimeException("Cannot register as JMX listener to netconf", e);
}
private void handleAfterCommitNotification(final CommitJMXNotification notification) {
try {
- persister.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(),
+ persisterAggregator.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(),
notification.getCapabilities(), ignoredMissingCapabilityRegex));
logger.info("Configuration persisted successfully");
} catch (IOException e) {
}
}
- private Optional<ConfigSnapshotHolder> loadLastConfig() {
- Optional<ConfigSnapshotHolder> maybeConfigElement;
- try {
- maybeConfigElement = persister.loadLastConfig();
- } catch (IOException e) {
- throw new RuntimeException("Unable to load configuration", e);
- }
- return maybeConfigElement;
- }
-
- private synchronized void pushLastConfig(Element xmlToBePersisted) throws ConflictingVersionException {
- logger.info("Pushing last configuration to netconf");
- StringBuilder response = new StringBuilder("editConfig response = {");
-
-
- NetconfMessage message = createEditConfigMessage(xmlToBePersisted, "/netconfOp/editConfig.xml");
-
- // sending message to netconf
- NetconfMessage responseMessage = netconfClient.sendMessage(message, NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY);
-
- XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument());
- Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
- element = element.getOnlyChildElement();
-
- checkIsOk(element, responseMessage);
- response.append(XmlUtil.toString(responseMessage.getDocument()));
- response.append("}");
- responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml"), NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY);
-
- element = XmlElement.fromDomDocument(responseMessage.getDocument());
- Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
- element = element.getOnlyChildElement();
-
- checkIsOk(element, responseMessage);
- response.append("commit response = {");
- response.append(XmlUtil.toString(responseMessage.getDocument()));
- response.append("}");
- logger.info("Last configuration loaded successfully");
- logger.trace("Detailed message {}", response);
- }
-
- static void checkIsOk(XmlElement element, NetconfMessage responseMessage) throws ConflictingVersionException {
- if (element.getName().equals(XmlNetconfConstants.OK)) {
- return;
- }
-
- if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) {
- logger.warn("Can not load last configuration, operation failed");
- // is it ConflictingVersionException ?
- XPathExpression xPathExpression = XMLNetconfUtil.compileXPath("/netconf:rpc-reply/netconf:rpc-error/netconf:error-info/netconf:error");
- String error = (String) XmlUtil.evaluateXPath(xPathExpression, element.getDomElement(), XPathConstants.STRING);
- if (error!=null && error.contains(ConflictingVersionException.class.getCanonicalName())) {
- throw new ConflictingVersionException(error);
- }
- throw new IllegalStateException("Can not load last configuration, operation failed: "
- + XmlUtil.toString(responseMessage.getDocument()));
- }
-
- logger.warn("Can not load last configuration. Operation failed.");
- throw new IllegalStateException("Can not load last configuration. Operation failed: "
- + XmlUtil.toString(responseMessage.getDocument()));
- }
-
- private static NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) {
- try (InputStream stream = ConfigPersisterNotificationHandler.class.getResourceAsStream(editConfigResourcename)) {
- Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcename);
-
- Document doc = XmlUtil.readXmlToDocument(stream);
-
- doc.getDocumentElement();
- XmlElement editConfigElement = XmlElement.fromDomDocument(doc).getOnlyChildElement();
- XmlElement configWrapper = editConfigElement.getOnlyChildElement(XmlNetconfConstants.CONFIG_KEY);
- editConfigElement.getDomElement().removeChild(configWrapper.getDomElement());
- for (XmlElement el : XmlElement.fromDomElement(dataElement).getChildElements()) {
- configWrapper.appendChild((Element) doc.importNode(el.getDomElement(), true));
- }
- editConfigElement.appendChild(configWrapper.getDomElement());
- return new NetconfMessage(doc);
- } catch (IOException | SAXException e) {
- throw new RuntimeException("Unable to parse message from resources " + editConfigResourcename, e);
- }
- }
-
- private NetconfMessage getNetconfMessageFromResource(String resource) {
- try (InputStream stream = getClass().getResourceAsStream(resource)) {
- Preconditions.checkNotNull(stream, "Unable to load resource " + resource);
- return new NetconfMessage(XmlUtil.readXmlToDocument(stream));
- } catch (SAXException | IOException e) {
- throw new RuntimeException("Unable to parse message from resources " + resource, e);
- }
- }
-
@Override
public synchronized void close() {
- // TODO persister is received from constructor, should not be closed
- // here
- try {
- persister.close();
- } catch (Exception e) {
- logger.warn("Unable to close config persister {}", persister, e);
- }
-
- if (netconfClient != null) {
- try {
- netconfClient.close();
- } catch (Exception e) {
- logger.warn("Unable to close connection to netconf {}", netconfClient, e);
- }
- }
-
- if (netconfClientDispatcher != null) {
- try {
- netconfClientDispatcher.close();
- } catch (Exception e) {
- logger.warn("Unable to close connection to netconf {}", netconfClientDispatcher, e);
- }
- }
-
- try {
- nettyThreadgroup.shutdownGracefully();
- } catch (Exception e) {
- logger.warn("Unable to close netconf client thread group {}", netconfClientDispatcher, e);
- }
-
// unregister from JMX
+ ObjectName on = DefaultCommitOperationMXBean.objectName;
try {
- if (mbeanServer.isRegistered(on)) {
- mbeanServer.removeNotificationListener(on, this);
+ if (mBeanServerConnection.isRegistered(on)) {
+ mBeanServerConnection.removeNotificationListener(on, this);
}
} catch (Exception e) {
logger.warn("Unable to unregister {} as listener for {}", this, on, e);
}
}
+
+ public NetconfClient getNetconfClient() {
+ return netconfClient;
+ }
+
}
--- /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 io.netty.channel.EventLoopGroup;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.annotation.concurrent.Immutable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Immutable
+public class ConfigPusher {
+ private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
+ private static final int NETCONF_SEND_ATTEMPT_MS_DELAY = 1000;
+ private static final int NETCONF_SEND_ATTEMPTS = 20;
+
+ private final InetSocketAddress address;
+ private final EventLoopGroup nettyThreadgroup;
+
+
+ public static final long DEFAULT_TIMEOUT = 120000L;// 120 seconds until netconf must be stable
+ private final long timeout;
+
+ public ConfigPusher(InetSocketAddress address, EventLoopGroup nettyThreadgroup) {
+ this(address, DEFAULT_TIMEOUT, nettyThreadgroup);
+
+ }
+
+ public ConfigPusher(InetSocketAddress address, long timeout, EventLoopGroup nettyThreadgroup) {
+ this.address = address;
+ this.timeout = timeout;
+
+ this.nettyThreadgroup = nettyThreadgroup;
+ }
+
+ public synchronized NetconfClient init(List<ConfigSnapshotHolder> configs) throws InterruptedException {
+ logger.debug("Last config snapshots to be pushed to netconf: {}", configs);
+ return pushAllConfigs(configs);
+ }
+
+ private synchronized NetconfClient pushAllConfigs(List<ConfigSnapshotHolder> configs) throws InterruptedException {
+ NetconfClient netconfClient = makeNetconfConnection(Collections.<String>emptySet(), Optional.<NetconfClient>absent());
+ for (ConfigSnapshotHolder configSnapshotHolder: configs){
+ netconfClient = pushSnapshotWithRetries(configSnapshotHolder, Optional.of(netconfClient));
+ }
+ return netconfClient;
+ }
+
+ private synchronized NetconfClient pushSnapshotWithRetries(ConfigSnapshotHolder configSnapshotHolder,
+ Optional<NetconfClient> oldClientForPossibleReuse)
+ throws InterruptedException {
+
+ ConflictingVersionException lastException = null;
+ int maxAttempts = 30;
+ for(int i = 0 ; i < maxAttempts; i++) {
+ NetconfClient netconfClient = makeNetconfConnection(configSnapshotHolder.getCapabilities(), oldClientForPossibleReuse);
+ final String configSnapshot = configSnapshotHolder.getConfigSnapshot();
+ logger.trace("Pushing following xml to netconf {}", configSnapshot);
+ try {
+ pushLastConfig(configSnapshotHolder, netconfClient);
+ return netconfClient;
+ } catch(ConflictingVersionException e) {
+ Util.closeClientAndDispatcher(netconfClient);
+ lastException = e;
+ Thread.sleep(1000);
+ } catch (SAXException | IOException e) {
+ throw new IllegalStateException("Unable to load last config", e);
+ }
+ }
+ throw new IllegalStateException("Failed to push configuration, maximum attempt count has been reached: "
+ + maxAttempts, lastException);
+ }
+
+ /**
+ * @param expectedCaps capabilities that server hello must contain. Will retry until all are found or throws RuntimeException.
+ * If empty set is provided, will only make sure netconf client successfuly connected to the server.
+ * @param oldClientForPossibleReuse if present, try to get expected capabilities from it before closing it and retrying with
+ * new client connection.
+ * @return NetconfClient that has all required capabilities from server.
+ */
+ private synchronized NetconfClient makeNetconfConnection(Set<String> expectedCaps,
+ Optional<NetconfClient> oldClientForPossibleReuse)
+ throws InterruptedException {
+
+ if (oldClientForPossibleReuse.isPresent()) {
+ NetconfClient oldClient = oldClientForPossibleReuse.get();
+ if (Util.isSubset(oldClient, expectedCaps)) {
+ return oldClient;
+ } else {
+ Util.closeClientAndDispatcher(oldClient);
+ }
+ }
+
+ // TODO think about moving capability subset check to netconf client
+ // could be utilized by integration tests
+
+ long pollingStart = System.currentTimeMillis();
+ int delay = 5000;
+
+ int attempt = 0;
+
+ long deadline = pollingStart + timeout;
+
+ Set<String> latestCapabilities = new HashSet<>();
+ while (System.currentTimeMillis() < deadline) {
+ attempt++;
+ NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup);
+ NetconfClient netconfClient;
+ try {
+ netconfClient = new NetconfClient(this.toString(), address, delay, netconfClientDispatcher);
+ } catch (IllegalStateException e) {
+ logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e);
+ netconfClientDispatcher.close();
+ Thread.sleep(delay);
+ continue;
+ }
+ latestCapabilities = netconfClient.getCapabilities();
+ if (Util.isSubset(netconfClient, expectedCaps)) {
+ logger.debug("Hello from netconf stable with {} capabilities", latestCapabilities);
+ logger.info("Session id received from netconf server: {}", netconfClient.getClientSession());
+ return netconfClient;
+ }
+ logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, latestCapabilities);
+ Util.closeClientAndDispatcher(netconfClient);
+ Thread.sleep(delay);
+ }
+ Set<String> allNotFound = new HashSet<>(expectedCaps);
+ allNotFound.removeAll(latestCapabilities);
+ logger.error("Netconf server did not provide required capabilities. Expected but not found: {}, all expected {}, current {}",
+ allNotFound, expectedCaps, latestCapabilities);
+ throw new RuntimeException("Netconf server did not provide required capabilities. Expected but not found:" + allNotFound);
+ }
+
+
+ private synchronized void pushLastConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfClient netconfClient)
+ throws ConflictingVersionException, IOException, SAXException {
+
+ Element xmlToBePersisted = XmlUtil.readXmlToElement(configSnapshotHolder.getConfigSnapshot());
+ logger.info("Pushing last configuration to netconf: {}", configSnapshotHolder);
+ StringBuilder response = new StringBuilder("editConfig response = {");
+
+
+ NetconfMessage message = createEditConfigMessage(xmlToBePersisted, "/netconfOp/editConfig.xml");
+
+ // sending message to netconf
+ NetconfMessage responseMessage = netconfClient.sendMessage(message, NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY);
+
+ XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument());
+ Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
+ element = element.getOnlyChildElement();
+
+ Util.checkIsOk(element, responseMessage);
+ response.append(XmlUtil.toString(responseMessage.getDocument()));
+ response.append("}");
+ responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml"), NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY);
+
+ element = XmlElement.fromDomDocument(responseMessage.getDocument());
+ Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
+ element = element.getOnlyChildElement();
+
+ Util.checkIsOk(element, responseMessage);
+ response.append("commit response = {");
+ response.append(XmlUtil.toString(responseMessage.getDocument()));
+ response.append("}");
+ logger.info("Last configuration loaded successfully");
+ logger.trace("Detailed message {}", response);
+ }
+
+ private static NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) {
+ try (InputStream stream = ConfigPersisterNotificationHandler.class.getResourceAsStream(editConfigResourcename)) {
+ Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcename);
+
+ Document doc = XmlUtil.readXmlToDocument(stream);
+
+ doc.getDocumentElement();
+ XmlElement editConfigElement = XmlElement.fromDomDocument(doc).getOnlyChildElement();
+ XmlElement configWrapper = editConfigElement.getOnlyChildElement(XmlNetconfConstants.CONFIG_KEY);
+ editConfigElement.getDomElement().removeChild(configWrapper.getDomElement());
+ for (XmlElement el : XmlElement.fromDomElement(dataElement).getChildElements()) {
+ configWrapper.appendChild((Element) doc.importNode(el.getDomElement(), true));
+ }
+ editConfigElement.appendChild(configWrapper.getDomElement());
+ return new NetconfMessage(doc);
+ } catch (IOException | SAXException e) {
+ throw new RuntimeException("Unable to parse message from resources " + editConfigResourcename, e);
+ }
+ }
+
+ private static NetconfMessage getNetconfMessageFromResource(String resource) {
+ try (InputStream stream = ConfigPusher.class.getResourceAsStream(resource)) {
+ Preconditions.checkNotNull(stream, "Unable to load resource " + resource);
+ return new NetconfMessage(XmlUtil.readXmlToDocument(stream));
+ } catch (SAXException | IOException e) {
+ throw new RuntimeException("Unable to parse message from resources " + resource, e);
+ }
+ }
+}
package org.opendaylight.controller.netconf.persist.impl;
-import com.google.common.base.Optional;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.api.PropertiesProvider;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
public class NoOpStorageAdapter implements StorageAdapter, Persister {
private static final Logger logger = LoggerFactory.getLogger(NoOpStorageAdapter.class);
}
@Override
- public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
logger.debug("loadLastConfig called");
- return Optional.absent();
+ return Collections.emptyList();
}
@Override
package org.opendaylight.controller.netconf.persist.impl;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.api.StorageAdapter;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
</pre>
* During server startup {@link ConfigPersisterNotificationHandler} requests last snapshot from underlying storages.
* Each storage can respond by giving snapshot or absent response.
- * The {@link #loadLastConfig()} will search for first non-absent response from storages ordered backwards as user
+ * The {@link #loadLastConfigs()} will search for first non-absent response from storages ordered backwards as user
* specified (first '3', then '2').
*
* When a commit notification is received, '2' will be omitted because readonly flag is set to true, so
}
}
+ /**
+ * @return last non-empty result from input persisters
+ */
@Override
- public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ public List<ConfigSnapshotHolder> loadLastConfigs() {
// iterate in reverse order
ListIterator<PersisterWithConfiguration> li = persisterWithConfigurations.listIterator(persisterWithConfigurations.size());
while(li.hasPrevious()) {
PersisterWithConfiguration persisterWithConfiguration = li.previous();
- Optional<ConfigSnapshotHolder> configSnapshotHolderOptional = persisterWithConfiguration.storage.loadLastConfig();
- if (configSnapshotHolderOptional.isPresent()) {
- return configSnapshotHolderOptional;
+ List<ConfigSnapshotHolder> configs = null;
+ try {
+ configs = persisterWithConfiguration.storage.loadLastConfigs();
+ } catch (IOException e) {
+ throw new RuntimeException("Error while calling loadLastConfig on " + persisterWithConfiguration, e);
+ }
+ if (configs.isEmpty() == false) {
+ logger.debug("Found non empty configs using {}:{}", persisterWithConfiguration, configs);
+ return configs;
}
}
// no storage had an answer
- return Optional.absent();
+ logger.debug("No non-empty list of configuration snapshots found");
+ return Collections.emptyList();
}
@VisibleForTesting
package org.opendaylight.controller.netconf.persist.impl;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadFactory;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import java.util.Set;
public final class Util {
+ private static final Logger logger = LoggerFactory.getLogger(Util.class);
+
+
+ public static boolean isSubset(NetconfClient netconfClient, Set<String> expectedCaps) {
+ return isSubset(netconfClient.getCapabilities(), expectedCaps);
+
+ }
+
+ private static boolean isSubset(Set<String> currentCapabilities, Set<String> expectedCaps) {
+ for (String exCap : expectedCaps) {
+ if (currentCapabilities.contains(exCap) == false)
+ return false;
+ }
+ return true;
+ }
+
+
+ // TODO: check if closing in correct order
+ public static void closeClientAndDispatcher(NetconfClient client) {
+ NetconfClientDispatcher dispatcher = client.getNetconfClientDispatcher();
+ Exception fromClient = null;
+ try {
+ client.close();
+ } catch (Exception e) {
+ fromClient = e;
+ } finally {
+ try {
+ dispatcher.close();
+ } catch (Exception e) {
+ if (fromClient != null) {
+ e.addSuppressed(fromClient);
+ }
+ throw new RuntimeException("Error closing temporary client ", e);
+ }
+ }
+ }
- public static ScheduledExecutorService getExecutorServiceWithThreadName(final String threadNamePrefix,
- int threadCount) {
- return Executors.newScheduledThreadPool(threadCount, new ThreadFactory() {
- private int i = 1;
+ public static void checkIsOk(XmlElement element, NetconfMessage responseMessage) throws ConflictingVersionException {
+ if (element.getName().equals(XmlNetconfConstants.OK)) {
+ return;
+ }
- @Override
- public Thread newThread(Runnable r) {
- Thread thread = new Thread(r);
- thread.setName(threadNamePrefix + ":" + i++);
- return thread;
+ if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) {
+ logger.warn("Can not load last configuration, operation failed");
+ // is it ConflictingVersionException ?
+ XPathExpression xPathExpression = XMLNetconfUtil.compileXPath("/netconf:rpc-reply/netconf:rpc-error/netconf:error-info/netconf:error");
+ String error = (String) XmlUtil.evaluateXPath(xPathExpression, element.getDomElement(), XPathConstants.STRING);
+ if (error!=null && error.contains(ConflictingVersionException.class.getCanonicalName())) {
+ throw new ConflictingVersionException(error);
}
- });
+ throw new IllegalStateException("Can not load last configuration, operation failed: "
+ + XmlUtil.toString(responseMessage.getDocument()));
+ }
+
+ logger.warn("Can not load last configuration. Operation failed.");
+ throw new IllegalStateException("Can not load last configuration. Operation failed: "
+ + XmlUtil.toString(responseMessage.getDocument()));
}
}
package org.opendaylight.controller.netconf.persist.impl.osgi;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import org.opendaylight.controller.netconf.client.NetconfClient;
import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
+import org.opendaylight.controller.netconf.persist.impl.ConfigPusher;
import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator;
+import org.opendaylight.controller.netconf.persist.impl.Util;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
private final static MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
private static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex";
- private ConfigPersisterNotificationHandler configPersisterNotificationHandler;
+ public static final String NETCONF_CONFIG_PERSISTER = "netconf.config.persister";
- private Thread initializationThread;
+ public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX = "storageAdapterClass";
- public static final String NETCONF_CONFIG_PERSISTER = "netconf.config.persister";
- public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX = "storageAdapterClass";
public static final String DEFAULT_IGNORED_REGEX = "^urn:ietf:params:xml:ns:netconf:base:1.0";
+
+ private volatile ConfigPersisterNotificationHandler jmxNotificationHandler;
+ private volatile NetconfClient netconfClient;
+ private Thread initializationThread;
+ private EventLoopGroup nettyThreadgroup;
+ private PersisterAggregator persisterAggregator;
+
@Override
public void start(final BundleContext context) throws Exception {
logger.debug("ConfigPersister starting");
} else {
regex = DEFAULT_IGNORED_REGEX;
}
- Pattern ignoredMissingCapabilityRegex = Pattern.compile(regex);
- PersisterAggregator persister = PersisterAggregator.createFromProperties(propertiesProvider);
+ final Pattern ignoredMissingCapabilityRegex = Pattern.compile(regex);
+ nettyThreadgroup = new NioEventLoopGroup();
+
+ persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider);
+ final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context, "Netconf is not configured, persister is not operational", true);
+ final ConfigPusher configPusher = new ConfigPusher(address, nettyThreadgroup);
- InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context,
- "Netconf is not configured, persister is not operational",true);
- configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(persister, address,
- platformMBeanServer, ignoredMissingCapabilityRegex);
// offload initialization to another thread in order to stop blocking activator
Runnable initializationRunnable = new Runnable() {
@Override
public void run() {
try {
- configPersisterNotificationHandler.init();
+ netconfClient = configPusher.init(persisterAggregator.loadLastConfigs());
+ jmxNotificationHandler = new ConfigPersisterNotificationHandler(
+ platformMBeanServer, netconfClient, persisterAggregator,
+ ignoredMissingCapabilityRegex);
+ jmxNotificationHandler.init();
} catch (InterruptedException e) {
logger.info("Interrupted while waiting for netconf connection");
}
@Override
public void stop(BundleContext context) throws Exception {
initializationThread.interrupt();
- configPersisterNotificationHandler.close();
+ if (jmxNotificationHandler != null) {
+ jmxNotificationHandler.close();
+ }
+ if (netconfClient != null) {
+ netconfClient = jmxNotificationHandler.getNetconfClient();
+ try {
+ Util.closeClientAndDispatcher(netconfClient);
+ } catch (Exception e) {
+ logger.warn("Unable to close connection to netconf {}", netconfClient, e);
+ }
+ }
+ nettyThreadgroup.shutdownGracefully();
+ persisterAggregator.close();
}
}
-<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <capabilities>
- <capability>urn:ietf:params:netconf:base:1.0</capability>
- </capabilities>
-</hello>
+ <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <capabilities>
+ <capability>urn:ietf:params:netconf:base:1.0</capability>
+ </capabilities>
+ </hello>
public void testConflictingVersionDetection() throws Exception {
Document document = XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/conflictingVersionResponse.xml"));
try{
- ConfigPersisterNotificationHandler.checkIsOk(XmlElement.fromDomDocument(document).getOnlyChildElement(), new NetconfMessage(document));
+ Util.checkIsOk(XmlElement.fromDomDocument(document).getOnlyChildElement(), new NetconfMessage(document));
fail();
}catch(ConflictingVersionException e){
assertThat(e.getMessage(), containsString("Optimistic lock failed. Expected parent version 21, was 18"));
*/
package org.opendaylight.controller.netconf.persist.impl;
-import com.google.common.base.Optional;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.api.PropertiesProvider;
import org.opendaylight.controller.config.persist.api.StorageAdapter;
import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
public class DummyAdapter implements StorageAdapter, Persister {
static int load = 0;
@Override
- public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+ public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
load++;
- return Optional.absent();
+ return Collections.emptyList();
}
static int props = 0;
package org.opendaylight.controller.netconf.persist.impl;
-import com.google.common.base.Optional;
import org.junit.Test;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Properties;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import static org.junit.matchers.JUnitMatchers.containsString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
assertFalse(persister.isReadOnly());
persisterAggregator.persistConfig(null);
- persisterAggregator.loadLastConfig();
+ persisterAggregator.loadLastConfigs();
persisterAggregator.persistConfig(null);
- persisterAggregator.loadLastConfig();
+ persisterAggregator.loadLastConfigs();
assertEquals(2, DummyAdapter.persist);
assertEquals(2, DummyAdapter.load);
}
}
+ private ConfigSnapshotHolder mockHolder(String name){
+ ConfigSnapshotHolder result = mock(ConfigSnapshotHolder.class);
+ doReturn("mock:" + name).when(result).toString();
+ return result;
+ }
+
+ private Persister mockPersister(String name){
+ Persister result = mock(Persister.class);
+ doReturn("mock:" + name).when(result).toString();
+ return result;
+ }
+
@Test
public void loadLastConfig() throws Exception {
List<PersisterWithConfiguration> persisterWithConfigurations = new ArrayList<>();
PersisterWithConfiguration first = new PersisterWithConfiguration(mock(Persister.class), false);
- ConfigSnapshotHolder ignored = mock(ConfigSnapshotHolder.class);
- doReturn(Optional.of(ignored)).when(first.getStorage()).loadLastConfig(); // should be ignored
+ ConfigSnapshotHolder ignored = mockHolder("ignored");
+ doReturn(Arrays.asList(ignored)).when(first.getStorage()).loadLastConfigs(); // should be ignored
- ConfigSnapshotHolder used = mock(ConfigSnapshotHolder.class);
- PersisterWithConfiguration second = new PersisterWithConfiguration(mock(Persister.class), false);
- doReturn(Optional.of(used)).when(second.getStorage()).loadLastConfig(); // should be used
- PersisterWithConfiguration third = new PersisterWithConfiguration(mock(Persister.class), false);
- doReturn(Optional.absent()).when(third.getStorage()).loadLastConfig();
+ ConfigSnapshotHolder used = mockHolder("used");
+ PersisterWithConfiguration second = new PersisterWithConfiguration(mockPersister("p1"), false);
+ doReturn(Arrays.asList(used)).when(second.getStorage()).loadLastConfigs(); // should be used
+
+ PersisterWithConfiguration third = new PersisterWithConfiguration(mockPersister("p2"), false);
+ doReturn(Arrays.asList()).when(third.getStorage()).loadLastConfigs();
persisterWithConfigurations.add(first);
persisterWithConfigurations.add(second);
persisterWithConfigurations.add(third);
PersisterAggregator persisterAggregator = new PersisterAggregator(persisterWithConfigurations);
- Optional<ConfigSnapshotHolder> configSnapshotHolderOptional = persisterAggregator.loadLastConfig();
- assertTrue(configSnapshotHolderOptional.isPresent());
- assertEquals(used, configSnapshotHolderOptional.get());
+ List<ConfigSnapshotHolder> configSnapshotHolderOptional = persisterAggregator.loadLastConfigs();
+ assertEquals(1, configSnapshotHolderOptional.size());
+ assertEquals(used, configSnapshotHolderOptional.get(0));
}
-
}
--- /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.3-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</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.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ target/generated-sources/monitoring
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>${yangtools.binding.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>target/generated-sources/sal</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ com.google.common.collect,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+ org.opendaylight.yangtools.yang.binding,
+ org.opendaylight.yangtools.yang.common,
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.*
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
--- /dev/null
+module ietf-netconf-monitoring {
+
+ yang-version 1;
+
+ namespace
+ "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+
+ prefix ncm;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+
+ WG Chair: Mehmet Ersue
+ <mailto:mehmet.ersue@nsn.com>
+
+ WG Chair: Bert Wijnen
+ <mailto:bertietf@bwijnen.net>
+
+ Editor: Mark Scott
+ <mailto:mark.scott@ericsson.com>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+ description
+ "NETCONF Monitoring Module.
+ All elements in this module are read-only.
+
+ Copyright (c) 2010 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD
+ License set forth in Section 4.c of the IETF Trust's
+ Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6022; see
+ the RFC itself for full legal notices.";
+
+ revision "2010-10-04" {
+ description "Initial revision.";
+ reference
+ "RFC 6022: YANG Module for NETCONF Monitoring";
+
+ }
+
+
+ typedef netconf-datastore-type {
+ type enumeration {
+ enum "running" {
+ value 0;
+ }
+ enum "candidate" {
+ value 1;
+ }
+ enum "startup" {
+ value 2;
+ }
+ }
+ description
+ "Enumeration of possible NETCONF datastore types.";
+ reference
+ "RFC 4741: NETCONF Configuration Protocol";
+
+ }
+
+ identity transport {
+ description
+ "Base identity for NETCONF transport types.";
+ }
+
+ identity netconf-ssh {
+ base transport;
+ description
+ "NETCONF over Secure Shell (SSH).";
+ reference
+ "RFC 4742: Using the NETCONF Configuration Protocol
+ over Secure SHell (SSH)";
+
+ }
+
+ identity netconf-soap-over-beep {
+ base transport;
+ description
+ "NETCONF over Simple Object Access Protocol (SOAP) over
+ Blocks Extensible Exchange Protocol (BEEP).";
+ reference
+ "RFC 4743: Using NETCONF over the Simple Object
+ Access Protocol (SOAP)";
+
+ }
+
+ identity netconf-soap-over-https {
+ base transport;
+ description
+ "NETCONF over Simple Object Access Protocol (SOAP)
+ over Hypertext Transfer Protocol Secure (HTTPS).";
+ reference
+ "RFC 4743: Using NETCONF over the Simple Object
+ Access Protocol (SOAP)";
+
+ }
+
+ identity netconf-beep {
+ base transport;
+ description
+ "NETCONF over Blocks Extensible Exchange Protocol (BEEP).";
+ reference
+ "RFC 4744: Using the NETCONF Protocol over the
+ Blocks Extensible Exchange Protocol (BEEP)";
+
+ }
+
+ identity netconf-tls {
+ base transport;
+ description
+ "NETCONF over Transport Layer Security (TLS).";
+ reference
+ "RFC 5539: NETCONF over Transport Layer Security (TLS)";
+
+ }
+
+ identity schema-format {
+ description
+ "Base identity for data model schema languages.";
+ }
+
+ identity xsd {
+ base schema-format;
+ description
+ "W3C XML Schema Definition.";
+ reference
+ "W3C REC REC-xmlschema-1-20041028:
+ XML Schema Part 1: Structures";
+
+ }
+
+ identity yang {
+ base schema-format;
+ description
+ "The YANG data modeling language for NETCONF.";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for the
+ Network Configuration Protocol (NETCONF)";
+
+ }
+
+ identity yin {
+ base schema-format;
+ description "The YIN syntax for YANG.";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for the
+ Network Configuration Protocol (NETCONF)";
+
+ }
+
+ identity rng {
+ base schema-format;
+ description
+ "Regular Language for XML Next Generation (RELAX NG).";
+ reference
+ "ISO/IEC 19757-2:2008: RELAX NG";
+
+ }
+
+ identity rnc {
+ base schema-format;
+ description "Relax NG Compact Syntax";
+ reference
+ "ISO/IEC 19757-2:2008: RELAX NG";
+
+ }
+
+ grouping common-counters {
+ description
+ "Counters that exist both per session, and also globally,
+ accumulated from all sessions.";
+ leaf in-rpcs {
+ type yang:zero-based-counter32;
+ description
+ "Number of correct <rpc> messages received.";
+ }
+
+ leaf in-bad-rpcs {
+ type yang:zero-based-counter32;
+ description
+ "Number of messages received when an <rpc> message was expected,
+ that were not correct <rpc> messages. This includes XML parse
+ errors and errors on the rpc layer.";
+ }
+
+ leaf out-rpc-errors {
+ type yang:zero-based-counter32;
+ description
+ "Number of <rpc-reply> messages sent that contained an
+ <rpc-error> element.";
+ }
+
+ leaf out-notifications {
+ type yang:zero-based-counter32;
+ description
+ "Number of <notification> messages sent.";
+ }
+ } // grouping common-counters
+
+ container netconf-state {
+ config false;
+ description
+ "The netconf-state container is the root of the monitoring
+ data model.";
+ container capabilities {
+ description
+ "Contains the list of NETCONF capabilities supported by the
+ server.";
+ leaf-list capability {
+ type inet:uri;
+ description
+ "List of NETCONF capabilities supported by the server.";
+ }
+ } // container capabilities
+
+ container datastores {
+ description
+ "Contains the list of NETCONF configuration datastores.";
+ list datastore {
+ key "name";
+ description
+ "List of NETCONF configuration datastores supported by
+ the NETCONF server and related information.";
+ leaf name {
+ type netconf-datastore-type;
+ description
+ "Name of the datastore associated with this list entry.";
+ }
+
+ container locks {
+ presence
+ "This container is present only if the datastore
+ is locked.";
+ description
+ "The NETCONF <lock> and <partial-lock> operations allow
+ a client to lock specific resources in a datastore. The
+ NETCONF server will prevent changes to the locked
+ resources by all sessions except the one that acquired
+ the lock(s).
+
+ Monitoring information is provided for each datastore
+ entry including details such as the session that acquired
+ the lock, the type of lock (global or partial) and the
+ list of locked resources. Multiple locks per datastore
+ are supported.";
+ grouping lock-info {
+ description
+ "Lock related parameters, common to both global and
+ partial locks.";
+ leaf locked-by-session {
+ type uint32;
+ mandatory true;
+ description
+ "The session ID of the session that has locked
+ this resource. Both a global lock and a partial
+ lock MUST contain the NETCONF session-id.
+
+ If the lock is held by a session that is not managed
+ by the NETCONF server (e.g., a CLI session), a session
+ id of 0 (zero) is reported.";
+ reference
+ "RFC 4741: NETCONF Configuration Protocol";
+
+ }
+
+ leaf locked-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "The date and time of when the resource was
+ locked.";
+ }
+ } // grouping lock-info
+ choice lock-type {
+ description
+ "Indicates if a global lock or a set of partial locks
+ are set.";
+ container global-lock {
+ description
+ "Present if the global lock is set.";
+ uses lock-info;
+ } // container global-lock
+ list partial-lock {
+ key "lock-id";
+ description
+ "List of partial locks.";
+ reference
+ "RFC 5717: Partial Lock Remote Procedure Call (RPC) for
+ NETCONF";
+
+ leaf lock-id {
+ type uint32;
+ description
+ "This is the lock id returned in the <partial-lock>
+ response.";
+ }
+
+ uses lock-info;
+
+ leaf-list select {
+ type yang:xpath1.0;
+ min-elements 1;
+ description
+ "The xpath expression that was used to request
+ the lock. The select expression indicates the
+ original intended scope of the lock.";
+ }
+
+ leaf-list locked-node {
+ type instance-identifier;
+ description
+ "The list of instance-identifiers (i.e., the
+ locked nodes).
+
+ The scope of the partial lock is defined by the list
+ of locked nodes.";
+ }
+ } // list partial-lock
+ } // choice lock-type
+ } // container locks
+ } // list datastore
+ } // container datastores
+
+ container schemas {
+ description
+ "Contains the list of data model schemas supported by the
+ server.";
+ list schema {
+ key "identifier version format";
+ description
+ "List of data model schemas supported by the server.";
+ leaf identifier {
+ type string;
+ description
+ "Identifier to uniquely reference the schema. The
+ identifier is used in the <get-schema> operation and may
+ be used for other purposes such as file retrieval.
+
+ For modeling languages that support or require a data
+ model name (e.g., YANG module name) the identifier MUST
+ match that name. For YANG data models, the identifier is
+ the name of the module or submodule. In other cases, an
+ identifier such as a filename MAY be used instead.";
+ }
+
+ leaf version {
+ type string;
+ description
+ "Version of the schema supported. Multiple versions MAY be
+ supported simultaneously by a NETCONF server. Each
+ version MUST be reported individually in the schema list,
+ i.e., with same identifier, possibly different location,
+ but different version.
+
+ For YANG data models, version is the value of the most
+ recent YANG 'revision' statement in the module or
+ submodule, or the empty string if no 'revision' statement
+ is present.";
+ }
+
+ leaf format {
+ type identityref {
+ base schema-format;
+ }
+ description
+ "The data modeling language the schema is written
+ in (currently xsd, yang, yin, rng, or rnc).
+ For YANG data models, 'yang' format MUST be supported and
+ 'yin' format MAY also be provided.";
+ }
+
+ leaf namespace {
+ type inet:uri;
+ mandatory true;
+ description
+ "The XML namespace defined by the data model.
+
+ For YANG data models, this is the module's namespace.
+ If the list entry describes a submodule, this field
+ contains the namespace of the module to which the
+ submodule belongs.";
+ }
+
+ leaf-list location {
+ type union {
+ type enumeration {
+ enum "NETCONF" {
+ value 0;
+ }
+ }
+ type inet:uri;
+ }
+ description
+ "One or more locations from which the schema can be
+ retrieved. This list SHOULD contain at least one
+ entry per schema.
+
+ A schema entry may be located on a remote file system
+ (e.g., reference to file system for ftp retrieval) or
+ retrieved directly from a server supporting the
+ <get-schema> operation (denoted by the value 'NETCONF').";
+ }
+ } // list schema
+ } // container schemas
+
+ container sessions {
+ description
+ "The sessions container includes session-specific data for
+ NETCONF management sessions. The session list MUST include
+ all currently active NETCONF sessions.";
+ list session {
+ key "session-id";
+ description
+ "All NETCONF sessions managed by the NETCONF server
+ MUST be reported in this list.";
+ leaf session-id {
+ type uint32 {
+ range "1..max";
+ }
+ description
+ "Unique identifier for the session. This value is the
+ NETCONF session identifier, as defined in RFC 4741.";
+ reference
+ "RFC 4741: NETCONF Configuration Protocol";
+
+ }
+
+ leaf transport {
+ type identityref {
+ base transport;
+ }
+ mandatory true;
+ description
+ "Identifies the transport for each session, e.g.,
+ 'netconf-ssh', 'netconf-soap', etc.";
+ }
+
+ leaf username {
+ type string;
+ mandatory true;
+ description
+ "The username is the client identity that was authenticated
+ by the NETCONF transport protocol. The algorithm used to
+ derive the username is NETCONF transport protocol specific
+ and in addition specific to the authentication mechanism
+ used by the NETCONF transport protocol.";
+ }
+
+ leaf source-host {
+ type inet:host;
+ description
+ "Host identifier of the NETCONF client. The value
+ returned is implementation specific (e.g., hostname,
+ IPv4 address, IPv6 address)";
+ }
+
+ leaf login-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "Time at the server at which the session was established.";
+ }
+
+ uses common-counters {
+ description
+ "Per-session counters. Zero based with following reset
+ behaviour:
+ - at start of a session
+ - when max value is reached";
+ }
+ } // list session
+ } // container sessions
+
+ container statistics {
+ description
+ "Statistical data pertaining to the NETCONF server.";
+ leaf netconf-start-time {
+ type yang:date-and-time;
+ description
+ "Date and time at which the management subsystem was
+ started.";
+ }
+
+ leaf in-bad-hellos {
+ type yang:zero-based-counter32;
+ description
+ "Number of sessions silently dropped because an
+ invalid <hello> message was received. This includes <hello>
+ messages with a 'session-id' attribute, bad namespace, and
+ bad capability declarations.";
+ }
+
+ leaf in-sessions {
+ type yang:zero-based-counter32;
+ description
+ "Number of sessions started. This counter is incremented
+ when a <hello> message with a <session-id> is sent.
+
+ 'in-sessions' - 'in-bad-hellos' =
+ 'number of correctly started netconf sessions'";
+ }
+
+ leaf dropped-sessions {
+ type yang:zero-based-counter32;
+ description
+ "Number of sessions that were abnormally terminated, e.g.,
+ due to idle timeout or transport close. This counter is not
+ incremented when a session is properly closed by a
+ <close-session> operation, or killed by a <kill-session>
+ operation.";
+ }
+
+ uses common-counters {
+ description
+ "Global counters, accumulated from all sessions.
+ Zero based with following reset behaviour:
+ - re-initialization of NETCONF server
+ - when max value is reached";
+ }
+ } // container statistics
+ } // container netconf-state
+
+ rpc get-schema {
+ description
+ "This operation is used to retrieve a schema from the
+ NETCONF server.
+
+ Positive Response:
+ The NETCONF server returns the requested schema.
+
+ Negative Response:
+ If requested schema does not exist, the <error-tag> is
+ 'invalid-value'.
+
+ If more than one schema matches the requested parameters, the
+ <error-tag> is 'operation-failed', and <error-app-tag> is
+ 'data-not-unique'.";
+ input {
+ leaf identifier {
+ type string;
+ mandatory true;
+ description
+ "Identifier for the schema list entry.";
+ }
+
+ leaf version {
+ type string;
+ description
+ "Version of the schema requested. If this parameter is not
+ present, and more than one version of the schema exists on
+ the server, a 'data-not-unique' error is returned, as
+ described above.";
+ }
+
+ leaf format {
+ type identityref {
+ base schema-format;
+ }
+ description
+ "The data modeling language of the schema. If this
+ parameter is not present, and more than one formats of
+ the schema exists on the server, a 'data-not-unique' error
+ is returned, as described above.";
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Contains the schema content.";
+ }
+ }
+ } // rpc get-schema
+} // module
\ No newline at end of file
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ </dependency>
+
<dependency>
<groupId>org.opendaylight.bgpcep</groupId>
<artifactId>framework</artifactId>
io.netty.channel,
io.netty.util.concurrent,
org.w3c.dom,
- org.slf4j
+ org.slf4j,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions,
+ com.google.common.base,
</Import-Package>
<Export-Package>
org.opendaylight.controller.netconf.api,
org.opendaylight.controller.netconf.api.jmx,
+ org.opendaylight.controller.netconf.api.monitoring,
</Export-Package>
</instructions>
</configuration>
package org.opendaylight.controller.netconf.api;
+import com.google.common.base.Optional;
import org.w3c.dom.Document;
/**
private final Document doc;
+ private String additionalHeader;
+
public NetconfMessage(final Document doc) {
+ this(doc, null);
+ }
+
+ public NetconfMessage(Document doc, String additionalHeader) {
this.doc = doc;
+ this.additionalHeader = additionalHeader;
}
public Document getDocument() {
return this.doc;
}
+
+ public Optional<String> getAdditionalHeader() {
+ return additionalHeader== null ? Optional.<String>absent() : Optional.of(additionalHeader);
+ }
}
@Override
public void close() {
channel.close();
+ up = false;
sessionListener.onSessionTerminated(this, new NetconfTerminationReason("Session closed"));
}
--- /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.monitoring;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+
+public interface NetconfManagementSession {
+
+ Session toManagementSession();
+}
--- /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.monitoring;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+
+public interface NetconfMonitoringService {
+
+ Sessions getSessions();
+
+ Schemas getSchemas();
+}
clientSession.close();
}
+ public NetconfClientDispatcher getNetconfClientDispatcher() {
+ return dispatch;
+ }
+
private static ReconnectStrategy getReconnectStrategy(int connectionAttempts, int attemptMsTimeout) {
return new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, attemptMsTimeout, 1000, 1.0, null,
Long.valueOf(connectionAttempts), null);
public Collection<String> getServerCapabilities() {
return capabilities;
}
+
}
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.AbstractNetconfSessionNegotiator;
import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
}
@Override
- protected NetconfClientSession getSession(SessionListener sessionListener, Channel channel, Document doc) {
- return new NetconfClientSession(sessionListener, channel, extractSessionId(doc), getCapabilities(doc));
+ protected NetconfClientSession getSession(SessionListener sessionListener, Channel channel, NetconfMessage message) {
+ return new NetconfClientSession(sessionListener, channel, extractSessionId(message.getDocument()),
+ getCapabilities(message.getDocument()));
}
}
<groupId>${project.groupId}</groupId>
<artifactId>netconf-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-util</artifactId>
<artifactId>netconf-mapping-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+
<dependency>
<groupId>org.opendaylight.bgpcep</groupId>
<artifactId>util</artifactId>
io.netty.buffer,
io.netty.handler.codec,
io.netty.channel.nio,
+ javax.annotation,
javax.management,
javax.net.ssl,
javax.xml.namespace,
org.w3c.dom,
org.xml.sax,
org.opendaylight.controller.netconf.util.messages,
- com.siemens.ct.exi.exceptions
+ com.siemens.ct.exi.exceptions,
+ io.netty.util.internal,
+ org.opendaylight.controller.netconf.api.monitoring,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas,
</Import-Package>
</instructions>
</configuration>
package org.opendaylight.controller.netconf.impl;
+import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
-
import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
import org.opendaylight.protocol.framework.SessionListener;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Transport;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.SessionBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.SessionKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.DateAndTime;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.ZeroBasedCounter32;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class NetconfServerSession extends NetconfSession {
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class NetconfServerSession extends NetconfSession implements NetconfManagementSession {
private static final Logger logger = LoggerFactory.getLogger(NetconfServerSession.class);
- public NetconfServerSession(SessionListener sessionListener, Channel channel, long sessionId) {
- super(sessionListener,channel,sessionId);
+ private final NetconfServerSessionNegotiator.AdditionalHeader header;
+
+ private Date loginTime;
+ private long inRpcSuccess, inRpcFail, outRpcError;
+
+ public NetconfServerSession(SessionListener sessionListener, Channel channel, long sessionId,
+ NetconfServerSessionNegotiator.AdditionalHeader header) {
+ super(sessionListener, channel, sessionId);
+ this.header = header;
logger.debug("Session {} created", toString());
}
+
+ @Override
+ protected void sessionUp() {
+ super.sessionUp();
+ Preconditions.checkState(loginTime == null, "Session is already up");
+ this.loginTime = new Date();
+ }
+
+ public void onIncommingRpcSuccess() {
+ inRpcSuccess++;
+ }
+
+ public void onIncommingRpcFail() {
+ inRpcFail++;
+ }
+
+ public void onOutgoingRpcError() {
+ outRpcError++;
+ }
+
+ public static final String ISO_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
+
+ @Override
+ public Session toManagementSession() {
+ SessionBuilder builder = new SessionBuilder();
+
+ builder.setSessionId(getSessionId());
+ builder.setSourceHost(new Host(new DomainName(header.getAddress())));
+
+ Preconditions.checkState(DateAndTime.PATTERN_CONSTANTS.size() == 1);
+ String formattedDateTime = formatDateTime(loginTime);
+ String pattern = DateAndTime.PATTERN_CONSTANTS.get(0);
+ Matcher matcher = Pattern.compile(pattern).matcher(formattedDateTime);
+ Preconditions.checkState(matcher.matches(), "Formatted datetime %s does not match pattern %s", formattedDateTime, pattern);
+ builder.setLoginTime(new DateAndTime(formattedDateTime));
+
+ builder.setInBadRpcs(new ZeroBasedCounter32(inRpcFail));
+ builder.setInRpcs(new ZeroBasedCounter32(inRpcSuccess));
+ builder.setOutRpcErrors(new ZeroBasedCounter32(outRpcError));
+
+ builder.setUsername(header.getUsername());
+ builder.setTransport(getTransportForString(header.getTransport()));
+
+ builder.setOutNotifications(new ZeroBasedCounter32(0L));
+
+ builder.setKey(new SessionKey(getSessionId()));
+ return builder.build();
+ }
+
+ private Class<? extends Transport> getTransportForString(String transport) {
+ switch(transport) {
+ case "ssh" : return NetconfSsh.class;
+ // TODO what about tcp
+ case "tcp" : return NetconfSsh.class;
+ default: throw new IllegalArgumentException("Unknown transport type " + transport);
+ }
+ }
+
+ private String formatDateTime(Date loginTime) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat(ISO_DATE_FORMAT);
+ return dateFormat.format(loginTime);
+ }
+
}
package org.opendaylight.controller.netconf.impl;
-import static com.google.common.base.Preconditions.checkState;
-
+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.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
+import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
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.w3c.dom.Document;
import org.w3c.dom.Node;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
+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 final SessionMonitoringService monitoringService;
private NetconfOperationRouterImpl operationRouter;
- public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter) {
+ public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter,
+ SessionMonitoringService monitoringService) {
this.operationRouter = operationRouter;
+ this.monitoringService = monitoringService;
}
@Override
public void onSessionUp(NetconfServerSession netconfNetconfServerSession) {
-
+ monitoringService.onSessionUp(netconfNetconfServerSession);
}
@Override
public void onSessionDown(NetconfServerSession netconfNetconfServerSession, Exception e) {
logger.debug("Session {} down, reason: {}", netconfNetconfServerSession, e.getMessage());
+ monitoringService.onSessionDown(netconfNetconfServerSession);
operationRouter.close();
}
NetconfTerminationReason netconfTerminationReason) {
logger.debug("Session {} terminated, reason: {}", netconfNetconfServerSession,
netconfTerminationReason.getErrorMessage());
+ monitoringService.onSessionDown(netconfNetconfServerSession);
operationRouter.close();
}
// schemas
final NetconfMessage message = processDocument(netconfMessage,
session);
- logger.debug("Respondign with message {}", XmlUtil.toString(message.getDocument()));
+ logger.debug("Responding with message {}", XmlUtil.toString(message.getDocument()));
session.sendMessage(message);
if (isCloseSession(netconfMessage)) {
}
} catch (final RuntimeException e) {
- logger.error("Unexpected exception", e);
// TODO: should send generic error or close session?
+ logger.error("Unexpected exception", e);
+ session.onIncommingRpcFail();
throw new RuntimeException("Unable to process incoming message " + netconfMessage, e);
} catch (NetconfDocumentedException e) {
+ session.onOutgoingRpcError();
+ session.onIncommingRpcFail();
SendErrorExceptionUtil.sendErrorMessage(session, e, netconfMessage);
}
}
}
private NetconfMessage processDocument(final NetconfMessage netconfMessage,
- NetconfSession session) throws NetconfDocumentedException {
+ NetconfServerSession session) throws NetconfDocumentedException {
final Document incommingDocument = netconfMessage.getDocument();
final Node rootNode = incommingDocument.getDocumentElement();
final Document responseDocument = XmlUtil.newDocument();
Document rpcReply = operationRouter.onNetconfMessage(
incommingDocument, session);
+
+ session.onIncommingRpcSuccess();
+
responseDocument.appendChild(responseDocument.importNode(rpcReply.getDocumentElement(), true));
return new NetconfMessage(responseDocument);
} else {
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.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.protocol.framework.SessionListenerFactory;
public class NetconfServerSessionListenerFactory implements SessionListenerFactory<NetconfServerSessionListener> {
private final SessionIdProvider idProvider;
+ private final SessionMonitoringService monitor;
+
public NetconfServerSessionListenerFactory(NetconfOperationServiceFactoryListener factoriesListener,
- DefaultCommitNotificationProducer commitNotifier,
- SessionIdProvider idProvider) {
+ DefaultCommitNotificationProducer commitNotifier,
+ SessionIdProvider idProvider, SessionMonitoringService monitor) {
this.factoriesListener = factoriesListener;
this.commitNotifier = commitNotifier;
this.idProvider = idProvider;
+ this.monitor = monitor;
}
@Override
netconfOperationServiceSnapshot, capabilityProvider,
commitNotifier);
- return new NetconfServerSessionListener(operationRouter);
+ return new NetconfServerSessionListener(operationRouter, monitor);
}
}
package org.opendaylight.controller.netconf.impl;
+import com.google.common.base.Optional;
+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.NetconfServerSessionPreferences;
import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
import org.opendaylight.protocol.framework.SessionListener;
-import org.w3c.dom.Document;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import io.netty.channel.Channel;
-import io.netty.util.Timer;
-import io.netty.util.concurrent.Promise;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class NetconfServerSessionNegotiator extends
AbstractNetconfSessionNegotiator<NetconfServerSessionPreferences, NetconfServerSession> {
+ static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionNegotiator.class);
+
+ private static final AdditionalHeader DEFAULT_HEADER = new AdditionalHeader();
+
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());
+ protected NetconfServerSession getSession(SessionListener sessionListener, Channel channel, NetconfMessage message) {
+ Optional<String> additionalHeader = message.getAdditionalHeader();
+
+ AdditionalHeader parsedHeader;
+ if (additionalHeader.isPresent()) {
+ parsedHeader = new AdditionalHeader(additionalHeader.get());
+ } else {
+ parsedHeader = DEFAULT_HEADER;
+ }
+ logger.debug("Additional header from hello parsed as {} from {}", parsedHeader, additionalHeader);
+
+ return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId(), parsedHeader);
}
+ static class AdditionalHeader {
+
+ private static final Pattern pattern = Pattern
+ .compile("\\[(?<username>[^;]+);(?<address>[0-9\\.]+)[:/](?<port>[0-9]+);(?<transport>[a-z]+)[^\\]]+\\]");
+ private final String username;
+ private final String address;
+ private final String transport;
+
+ public AdditionalHeader(String addHeaderAsString) {
+ addHeaderAsString = addHeaderAsString.trim();
+ Matcher matcher = pattern.matcher(addHeaderAsString);
+ Preconditions.checkArgument(matcher.matches(), "Additional header in wrong format %s, expected %s",
+ addHeaderAsString, pattern);
+ this.username = matcher.group("username");
+ this.address = matcher.group("address");
+ this.transport = matcher.group("transport");
+ }
+
+ private AdditionalHeader() {
+ this.username = this.address = "unknown";
+ this.transport = "ssh";
+ }
+
+ String getUsername() {
+ return username;
+ }
+
+ String getAddress() {
+ return address;
+ }
+
+ String getTransport() {
+ return transport;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("AdditionalHeader{");
+ sb.append("username='").append(username).append('\'');
+ sb.append(", address='").append(address).append('\'');
+ sb.append(", transport='").append(transport).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+ }
}
package org.opendaylight.controller.netconf.impl.mapping.operations;
+import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
import java.util.HashMap;
import java.util.Map;
+import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
*/\r
package org.opendaylight.controller.netconf.impl.mapping.operations;\r
\r
+import org.opendaylight.controller.netconf.api.NetconfSession;\r
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;\r
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;\r
-import org.opendaylight.controller.netconf.api.NetconfSession;\r
import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;\r
import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;\r
import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;\r
*/\r
package org.opendaylight.controller.netconf.impl.mapping.operations;\r
\r
+import org.opendaylight.controller.netconf.api.NetconfSession;\r
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;\r
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;\r
-import org.opendaylight.controller.netconf.api.NetconfSession;\r
import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;\r
import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;\r
import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;\r
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionListenerFactory;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
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.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
public class NetconfImplActivator implements BundleActivator {
private static final Logger logger = LoggerFactory.getLogger(NetconfImplActivator.class);
private NetconfServerDispatcher dispatch;
private NioEventLoopGroup eventLoopGroup;
private HashedWheelTimer timer;
+ private ServiceRegistration<NetconfMonitoringService> regMonitoring;
@Override
public void start(final BundleContext context) throws Exception {
"TCP is not configured, netconf not available.", false);
NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
- factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
- factoriesTracker.open();
+ startOperationServiceFactoryTracker(context, factoriesListener);
SessionIdProvider idProvider = new SessionIdProvider();
timer = new HashedWheelTimer();
commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+ NetconfMonitoringServiceImpl monitoringService = startMonitoringService(context, factoriesListener);
+
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, monitoringService);
eventLoopGroup = new NioEventLoopGroup();
}
+ private void startOperationServiceFactoryTracker(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+ factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
+ factoriesTracker.open();
+ }
+
+ private NetconfMonitoringServiceImpl startMonitoringService(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+ NetconfMonitoringServiceImpl netconfMonitoringServiceImpl = new NetconfMonitoringServiceImpl(factoriesListener);
+ Dictionary<String, ?> dic = new Hashtable<>();
+ regMonitoring = context.registerService(NetconfMonitoringService.class, netconfMonitoringServiceImpl, dic);
+
+ return netconfMonitoringServiceImpl;
+ }
+
@Override
public void stop(final BundleContext context) throws Exception {
logger.info("Shutting down netconf because YangStoreService service was removed");
commitNot.close();
eventLoopGroup.shutdownGracefully();
timer.stop();
+
+ regMonitoring.unregister();
+ factoriesTracker.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.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import io.netty.util.internal.ConcurrentSet;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Yang;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SessionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.SchemaBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.SchemaKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class NetconfMonitoringServiceImpl implements NetconfMonitoringService, SessionMonitoringService {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfMonitoringServiceImpl.class);
+
+ private final Set<NetconfManagementSession> sessions = new ConcurrentSet<>();
+ private final NetconfOperationServiceFactoryListener factoriesListener;
+
+ public NetconfMonitoringServiceImpl(NetconfOperationServiceFactoryListener factoriesListener) {
+ this.factoriesListener = factoriesListener;
+ }
+
+ @Override
+ public void onSessionUp(NetconfManagementSession session) {
+ logger.debug("Session {} up", session);
+ Preconditions.checkState(sessions.contains(session) == false, "Session %s was already added", session);
+ sessions.add(session);
+ }
+
+ @Override
+ public void onSessionDown(NetconfManagementSession session) {
+ logger.debug("Session {} down", session);
+ Preconditions.checkState(sessions.contains(session) == true, "Session %s not present", session);
+ sessions.remove(session);
+ }
+
+ @Override
+ public Sessions getSessions() {
+ return new SessionsBuilder().setSession(transformSessions(sessions)).build();
+ }
+
+ @Override
+ public Schemas getSchemas() {
+ // FIXME, session ID
+ return transformSchemas(factoriesListener.getSnapshot(0));
+ }
+
+ private Schemas transformSchemas(NetconfOperationServiceSnapshot snapshot) {
+ Set<Capability> caps = Sets.newHashSet();
+
+ List<Schema> schemas = Lists.newArrayList();
+
+ for (NetconfOperationService netconfOperationService : snapshot.getServices()) {
+ // TODO check for duplicates ? move capability merging to snapshot
+ caps.addAll(netconfOperationService.getCapabilities());
+ }
+
+ for (Capability cap : caps) {
+ SchemaBuilder builder = new SchemaBuilder();
+
+ if(cap.getCapabilitySchema().isPresent() == false)
+ continue;
+
+ Preconditions.checkState(cap.getModuleNamespace().isPresent());
+ builder.setNamespace(new Uri(cap.getModuleNamespace().get()));
+
+ Preconditions.checkState(cap.getRevision().isPresent());
+ String version = cap.getRevision().get();
+ builder.setVersion(version);
+
+ Preconditions.checkState(cap.getModuleName().isPresent());
+ String identifier = cap.getModuleName().get();
+ builder.setIdentifier(identifier);
+
+ builder.setFormat(Yang.class);
+
+ builder.setLocation(transformLocations(cap.getLocation().or(Collections.<String> emptyList())));
+
+ builder.setKey(new SchemaKey(Yang.class, identifier, version));
+
+ schemas.add(builder.build());
+ }
+
+ return new SchemasBuilder().setSchema(schemas).build();
+ }
+
+ private List<Schema.Location> transformLocations(List<String> locations) {
+ List<Schema.Location> monitoringLocations = Lists.newArrayList();
+ monitoringLocations.add(new Schema.Location(Schema.Location.Enumeration.NETCONF));
+
+ for (String location : locations) {
+ // TODO how to create enumerration from string location ?
+ // monitoringLocations.add(new Schema.Location(Schema.Location.Enumeration.valueOf(location)));
+ }
+
+ return monitoringLocations;
+ }
+
+ private List<Session> transformSessions(Set<NetconfManagementSession> sessions) {
+ return Lists.newArrayList(Collections2.transform(sessions, new Function<NetconfManagementSession, Session>() {
+ @Nullable
+ @Override
+ public Session apply(@Nullable NetconfManagementSession input) {
+ return input.toManagementSession();
+ }
+ }));
+ }
+}
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
public NetconfOperationRouterImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot,
- CapabilityProvider capabilityProvider,
- DefaultCommitNotificationProducer commitNotifier) {
+ CapabilityProvider capabilityProvider, DefaultCommitNotificationProducer commitNotifier) {
this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot;
.getNetconfSessionIdForReporting()));
defaultNetconfOperations.add(new DefaultCloseSession(netconfOperationServiceSnapshot
.getNetconfSessionIdForReporting()));
- defaultNetconfOperations.add(new DefaultStartExi(
- netconfOperationServiceSnapshot
- .getNetconfSessionIdForReporting()));
- defaultNetconfOperations.add(new DefaultStopExi(
- netconfOperationServiceSnapshot
- .getNetconfSessionIdForReporting()));
+ defaultNetconfOperations.add(new DefaultStartExi(netconfOperationServiceSnapshot
+ .getNetconfSessionIdForReporting()));
+ defaultNetconfOperations.add(new DefaultStopExi(netconfOperationServiceSnapshot
+ .getNetconfSessionIdForReporting()));
allNetconfOperations = getAllNetconfOperations(defaultNetconfOperations, netconfOperationServiceSnapshot);
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);
+ Preconditions.checkState(result.contains(filter) == false,
+ "Filter %s already present, all filters so far: %s", filter, result);
result.add(filter);
}
}
@Override
public synchronized Document onNetconfMessage(Document message,
NetconfSession session) throws NetconfDocumentedException {
- NetconfOperationExecution netconfOperationExecution = null;
+ NetconfOperationExecution netconfOperationExecution;
String messageAsString = XmlUtil.toString(message);
class NetconfOperationServiceFactoryTracker extends
ServiceTracker<NetconfOperationServiceFactory, NetconfOperationServiceFactory> {
- private final NetconfOperationServiceFactoryListener operationRouter;
+ private final NetconfOperationServiceFactoryListener factoriesListener;
NetconfOperationServiceFactoryTracker(BundleContext context,
- final NetconfOperationServiceFactoryListener operationRouter) {
+ final NetconfOperationServiceFactoryListener factoriesListener) {
super(context, NetconfOperationServiceFactory.class, null);
- this.operationRouter = operationRouter;
+ this.factoriesListener = factoriesListener;
}
@Override
public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
NetconfOperationServiceFactory netconfOperationServiceFactory = super.addingService(reference);
- operationRouter.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ factoriesListener.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
return netconfOperationServiceFactory;
}
@Override
public void removedService(ServiceReference<NetconfOperationServiceFactory> reference,
NetconfOperationServiceFactory netconfOperationServiceFactory) {
- operationRouter.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ factoriesListener.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 org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+
+public interface SessionMonitoringService {
+
+ void onSessionUp(NetconfManagementSession session);
+
+ void onSessionDown(NetconfManagementSession session);
+}
package org.opendaylight.controller.netconf.impl.util;
-import java.util.Map;
-
-import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
+import com.google.common.collect.Maps;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import com.google.common.collect.Maps;
+import java.util.Map;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
+public final class
+ DeserializerExceptionHandler implements ChannelHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(DeserializerExceptionHandler.class);
-public final class DeserializerExceptionHandler implements ChannelHandler {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
- if (cause instanceof NetconfDeserializerException) {
- handleDeserializerException(ctx, cause);
- }
+ logger.warn("An exception occured during message handling", cause);
+ handleDeserializerException(ctx, cause);
}
private void handleDeserializerException(ChannelHandlerContext ctx, Throwable cause) {
--- /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 junit.framework.Assert;
+import org.junit.Test;
+
+public class AdditionalHeaderParserTest {
+
+ @Test
+ public void testParsing() throws Exception {
+ String s = "[netconf;10.12.0.102:48528;ssh;;;;;;]";
+ NetconfServerSessionNegotiator.AdditionalHeader header = new NetconfServerSessionNegotiator.AdditionalHeader(s);
+ Assert.assertEquals("netconf", header.getUsername());
+ Assert.assertEquals("10.12.0.102", header.getAddress());
+ Assert.assertEquals("ssh", header.getTransport());
+ }
+
+ @Test
+ public void testParsing2() throws Exception {
+ String s = "[tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;]";
+ NetconfServerSessionNegotiator.AdditionalHeader header = new NetconfServerSessionNegotiator.AdditionalHeader(s);
+ Assert.assertEquals("tomas", header.getUsername());
+ Assert.assertEquals("10.0.0.0", header.getAddress());
+ Assert.assertEquals("tcp", header.getTransport());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParsingNoUsername() throws Exception {
+ String s = "[10.12.0.102:48528;ssh;;;;;;]";
+ new NetconfServerSessionNegotiator.AdditionalHeader(s);
+ }
+}
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
-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 javax.management.ObjectName;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.opendaylight.controller.netconf.client.NetconfClient;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
+
+import javax.management.ObjectName;
+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.fail;
import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
private DefaultCommitNotificationProducer commitNot;
private NetconfServerDispatcher dispatch;
+ @Mock
+ private SessionMonitoringService monitoring;
@Before
public void setUp() throws Exception {
commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+ doNothing().when(monitoring).onSessionUp(any(NetconfServerSession.class));
+ doNothing().when(monitoring).onSessionDown(any(NetconfServerSession.class));
+
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, monitoring);
NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(serverNegotiatorFactory, listenerFactory);
dispatch = new NetconfServerDispatcher(serverChannelInitializer, nettyGroup, nettyGroup);
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+
public class NetconfDispatcherImplTest {
private EventLoopGroup nettyGroup;
new HashedWheelTimer(), factoriesListener, idProvider);
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, null);
NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(serverNegotiatorFactory, listenerFactory);
<artifactId>netconf-impl</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-mapping-api</artifactId>
new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, NetconfITTest.getNetconfMonitoringListenerService());
NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
serverNegotiatorFactory, listenerFactory);
package org.opendaylight.controller.netconf.it;
-import ch.ethz.ssh2.Connection;
-import ch.ethz.ssh2.Session;
-import com.google.common.base.Optional;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import static java.util.Collections.emptyList;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
+
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-import java.util.regex.Pattern;
+
import javax.management.ObjectName;
import javax.xml.parsers.ParserConfigurationException;
+
import junit.framework.Assert;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
-import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
import org.opendaylight.controller.config.yang.store.api.YangStoreException;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;
import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
-import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.opendaylight.controller.netconf.util.xml.ExiParameters;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
-import static java.util.Collections.emptyList;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
public class NetconfITTest extends AbstractConfigTest {
- private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
- //
+ // TODO refactor, pull common code up to AbstractNetconfITTest
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830);
private NetconfClientDispatcher clientDispatcher;
-
@Before
public void setUp() throws Exception {
super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray(
new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, getNetconfMonitoringListenerService());
NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
serverNegotiatorFactory, listenerFactory);
return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
}
+ static NetconfMonitoringServiceImpl getNetconfMonitoringListenerService() {
+ NetconfOperationServiceFactoryListener factoriesListener = mock(NetconfOperationServiceFactoryListener.class);
+ NetconfOperationServiceSnapshot snap = mock(NetconfOperationServiceSnapshot.class);
+ doReturn(Collections.<NetconfOperationService>emptySet()).when(snap).getServices();
+ doReturn(snap).when(factoriesListener).getSnapshot(anyLong());
+ return new NetconfMonitoringServiceImpl(factoriesListener);
+ }
+
@After
public void tearDown() throws Exception {
commitNot.close();
}
}
+
+ //TODO: test persister actually
+ @Ignore
@Test(timeout = 10000)
public void testPersister() throws Exception {
- Persister persister = mock(Persister.class);
- doReturn("mockPersister").when(persister).toString();
- doReturn(Optional.absent()).when(persister).loadLastConfig();
- ConfigPersisterNotificationHandler h =
- new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer(), Pattern.compile(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX));
- h.init();
+// Persister persister = mock(Persister.class);
+// doReturn("mockPersister").when(persister).toString();
+// doReturn(Collections.emptyList()).when(persister).loadLastConfigs();
+// ConfigPersisterNotificationHandler h =
+// new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer(),
+// Pattern.compile(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX));
+// h.init();
}
@Ignore
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, clientDispatcher)) {
- // 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);
}
--- /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.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.util.HashedWheelTimer;
+import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.matchers.JUnitMatchers;
+import org.mockito.Mock;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.impl.HardcodedYangStoreService;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
+import org.opendaylight.controller.netconf.impl.NetconfServerSessionListenerFactory;
+import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
+import org.opendaylight.controller.netconf.impl.SessionIdProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator;
+import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class NetconfMonitoringITTest extends AbstractConfigTest {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
+
+ private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
+
+ @Mock
+ private DefaultCommitNotificationProducer commitNot;
+ private NetconfServerDispatcher dispatch;
+ private EventLoopGroup nettyThreadgroup;
+
+ private NetconfClientDispatcher clientDispatcher;
+
+ private NetconfMonitoringServiceImpl monitoringService;
+
+ @Before
+ public void setUp() throws Exception {
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(NetconfITTest.getModuleFactoriesS().toArray(
+ new ModuleFactory[0])));
+
+ monitoringService = new NetconfMonitoringServiceImpl(getFactoriesListener());
+
+ NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+ factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore()));
+ factoriesListener
+ .onAddNetconfOperationServiceFactory(new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
+ new NetconfMonitoringOperationService(monitoringService)));
+
+ nettyThreadgroup = new NioEventLoopGroup();
+
+ dispatch = createDispatcher(factoriesListener);
+ ChannelFuture s = dispatch.createServer(tcpAddress);
+ s.await();
+
+ clientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup);
+ }
+
+ private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException {
+ final Collection<InputStream> yangDependencies = NetconfITTest.getBasicYangs();
+ return new HardcodedYangStoreService(yangDependencies);
+ }
+
+ private NetconfServerDispatcher createDispatcher(
+ NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+ SessionIdProvider idProvider = new SessionIdProvider();
+ NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+ new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
+
+ NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+ factoriesListener, commitNot, idProvider, getNetconfMonitoringListenerService(logger, monitoringService));
+
+ NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
+ serverNegotiatorFactory, listenerFactory);
+ return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
+ }
+
+ static SessionMonitoringService getNetconfMonitoringListenerService(final Logger logger, final NetconfMonitoringServiceImpl monitor) {
+ return new SessionMonitoringService() {
+ @Override
+ public void onSessionUp(NetconfManagementSession session) {
+ logger.debug("Management session up {}", session);
+ monitor.onSessionUp(session);
+ }
+
+ @Override
+ public void onSessionDown(NetconfManagementSession session) {
+ logger.debug("Management session down {}", session);
+ monitor.onSessionDown(session);
+ }
+ };
+ }
+
+
+ @Test
+ public void testGetResponseFromMonitoring() throws Exception {
+ try (NetconfClient netconfClient = new NetconfClient("client-monitoring", tcpAddress, 4000, clientDispatcher)) {
+ try (NetconfClient netconfClient2 = new NetconfClient("client-monitoring2", tcpAddress, 4000, clientDispatcher)) {
+ NetconfMessage response = netconfClient.sendMessage(loadGetMessage());
+ assertSessionElementsInResponse(response.getDocument(), 2);
+ }
+ NetconfMessage response = netconfClient.sendMessage(loadGetMessage());
+ assertSessionElementsInResponse(response.getDocument(), 1);
+ }
+ }
+
+
+ @Test(timeout = 5 * 10000)
+ public void testClientHelloWithAuth() throws Exception {
+ String fileName = "netconfMessages/client_hello_with_auth.xml";
+ String hello = XmlFileLoader.fileToString(fileName);
+
+ fileName = "netconfMessages/get.xml";
+ String get = XmlFileLoader.fileToString(fileName);
+
+ Socket sock = new Socket(tcpAddress.getHostName(), tcpAddress.getPort());
+ sock.getOutputStream().write(hello.getBytes(Charsets.UTF_8));
+ String separator = "]]>]]>";
+
+ sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8));
+ sock.getOutputStream().write(get.getBytes(Charsets.UTF_8));
+ sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8));
+
+ StringBuilder responseBuilder = new StringBuilder();
+
+ try (InputStream inputStream = sock.getInputStream();
+ InputStreamReader reader = new InputStreamReader(inputStream);
+ BufferedReader buff = new BufferedReader(reader)) {
+ String line;
+ while ((line = buff.readLine()) != null) {
+
+ responseBuilder.append(line);
+ responseBuilder.append(System.lineSeparator());
+ System.out.println(responseBuilder.toString());
+
+ if(line.contains("</rpc-reply>"))
+ break;
+ }
+ }
+
+ org.junit.Assert.assertThat(responseBuilder.toString(), JUnitMatchers.containsString("<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>"));
+ org.junit.Assert.assertThat(responseBuilder.toString(), JUnitMatchers.containsString("<username>tomas</username>"));
+ }
+
+ private void assertSessionElementsInResponse(Document document, int i) {
+ int elementSize = document.getElementsByTagName("session-id").getLength();
+ Assert.assertEquals(i, elementSize);
+ }
+
+ private NetconfMessage loadGetMessage() throws Exception {
+ return XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/get.xml");
+ }
+
+ public NetconfOperationServiceFactoryListener getFactoriesListener() {
+ NetconfOperationServiceFactoryListener factoriesListener = mock(NetconfOperationServiceFactoryListener.class);
+ NetconfOperationServiceSnapshot snap = mock(NetconfOperationServiceSnapshot.class);
+ NetconfOperationService service = mock(NetconfOperationService.class);
+ Set<Capability> caps = Sets.newHashSet();
+ caps.add(new Capability() {
+ @Override
+ public String getCapabilityUri() {
+ return "namespaceModuleRevision";
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of("namespace");
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of("name");
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of("revision");
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.of("content");
+ }
+
+ @Override
+ public Optional<List<String>> getLocation() {
+ return Optional.absent();
+ }
+ });
+
+ doReturn(caps).when(service).getCapabilities();
+ Set<NetconfOperationService> services = Sets.newHashSet(service);
+ doReturn(services).when(snap).getServices();
+ doReturn(snap).when(factoriesListener).getSnapshot(anyLong());
+
+ return factoriesListener;
+ }
+
+
+}
import com.google.common.base.Optional;
+import java.util.List;
+
/**
* Contains capability URI announced by server hello message and optionally its
* corresponding yang schema that can be retrieved by get-schema rpc.
public String getCapabilityUri();
+ public Optional<String> getModuleNamespace();
+
public Optional<String> getModuleName();
public Optional<String> getRevision();
public Optional<String> getCapabilitySchema();
+
+ public Optional<List<String>> getLocation();
}
--- /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.3-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>netconf-monitoring</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <version>${bgpcep.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.monitoring.osgi.NetconfMonitoringActivator</Bundle-Activator>
+ <Import-Package>
+ com.google.common.base,
+ com.google.common.collect,
+ com.google.common.io,
+ org.opendaylight.controller.netconf.api,
+ org.opendaylight.controller.netconf.mapping.api,
+ org.opendaylight.controller.netconf.util.mapping,
+ org.osgi.framework,
+ org.slf4j,
+ org.w3c.dom,
+ javax.xml.bind,
+ javax.xml.bind.annotation,
+ javax.xml.transform,
+ javax.xml.transform.dom,
+ org.opendaylight.controller.netconf.util.xml,
+ io.netty.util.internal,
+ javax.annotation,
+ org.opendaylight.controller.netconf.api.monitoring,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+ org.osgi.util.tracker,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas,
+ </Import-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.monitoring;
+
+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.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilterChain;
+import org.opendaylight.controller.netconf.monitoring.xml.JaxBSerializer;
+import org.opendaylight.controller.netconf.monitoring.xml.model.NetconfState;
+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.Map;
+
+public class Get implements NetconfOperationFilter {
+
+ private static final Logger logger = LoggerFactory.getLogger(Get.class);
+ private final NetconfMonitoringService netconfMonitor;
+
+ public Get(NetconfMonitoringService netconfMonitor) {
+ this.netconfMonitor = netconfMonitor;
+ }
+
+ @Override
+ public Document doFilter(Document message, NetconfOperationRouter operationRouter,
+ NetconfOperationFilterChain filterChain) throws NetconfDocumentedException {
+ AbstractNetconfOperation.OperationNameAndNamespace operationNameAndNamespace = new AbstractNetconfOperation.OperationNameAndNamespace(
+ message);
+ if (canHandle(operationNameAndNamespace)) {
+ return handle(message, operationRouter, filterChain);
+ }
+ return filterChain.execute(message, operationRouter);
+ }
+
+ private Document handle(Document message, NetconfOperationRouter operationRouter,
+ NetconfOperationFilterChain filterChain) throws NetconfDocumentedException {
+ try {
+ Document innerResult = filterChain.execute(message, operationRouter);
+
+ NetconfState netconfMonitoring = new NetconfState(netconfMonitor);
+ Element monitoringXmlElement = new JaxBSerializer().toXml(netconfMonitoring);
+
+ monitoringXmlElement = (Element) innerResult.importNode(monitoringXmlElement, true);
+ Element monitoringXmlElementPlaceholder = getPlaceholder(innerResult);
+ monitoringXmlElementPlaceholder.appendChild(monitoringXmlElement);
+
+ return innerResult;
+ } catch (RuntimeException e) {
+ String errorMessage = "Get operation for netconf-state subtree failed";
+ logger.warn(errorMessage, e);
+ Map<String, String> info = Maps.newHashMap();
+ info.put(NetconfDocumentedException.ErrorSeverity.error.toString(), e.getMessage());
+ throw new NetconfDocumentedException(errorMessage, NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_failed,
+ NetconfDocumentedException.ErrorSeverity.error, info);
+ }
+ }
+
+ private Element getPlaceholder(Document innerResult) {
+ try {
+ XmlElement rootElement = XmlElement.fromDomElementWithExpected(innerResult.getDocumentElement(),
+ XmlNetconfConstants.RPC_REPLY_KEY, XmlNetconfConstants.RFC4741_TARGET_NAMESPACE);
+ return rootElement.getOnlyChildElement(XmlNetconfConstants.DATA_KEY).getDomElement();
+ } catch (RuntimeException e) {
+ throw new IllegalArgumentException(String.format(
+ "Input xml in wrong format, Expecting root element %s with child element %s, but was %s",
+ XmlNetconfConstants.RPC_REPLY_KEY, XmlNetconfConstants.DATA_KEY,
+ XmlUtil.toString(innerResult.getDocumentElement())), e);
+ }
+ }
+
+ private boolean canHandle(AbstractNetconfOperation.OperationNameAndNamespace operationNameAndNamespace) {
+ if (operationNameAndNamespace.getOperationName().equals(XmlNetconfConstants.GET) == false)
+ return false;
+ return operationNameAndNamespace.getNamespace().equals(
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ }
+
+ @Override
+ public int getSortingOrder() {
+ // FIXME filters for different operations cannot have same order
+ return 1;
+ }
+
+ @Override
+ public int compareTo(NetconfOperationFilter o) {
+ return Integer.compare(getSortingOrder(), o.getSortingOrder());
+ }
+
+}
--- /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.monitoring;
+
+public class MonitoringConstants {
+
+ public static final String NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+ public static final String MODULE_NAME = "ietf-netconf-monitoring";
+ public static final String MODULE_REVISION = "2010-10-04";
+
+ public static final String URI = String.format("%s?module=%s&revision=%s", NAMESPACE, MODULE_NAME, MODULE_REVISION);
+
+ public static final String NETCONF_MONITORING_XML_ROOT_ELEMENT = "netconf-state";
+}
--- /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.monitoring.osgi;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfMonitoringActivator implements BundleActivator {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfMonitoringActivator.class);
+
+ private NetconfMonitoringServiceTracker monitor;
+
+ @Override
+ public void start(final BundleContext context) throws Exception {
+ monitor = new NetconfMonitoringServiceTracker(context);
+ monitor.open();
+
+ }
+
+ @Override
+ public void stop(final BundleContext context) throws Exception {
+ if(monitor!=null) {
+ try {
+ monitor.close();
+ } catch (Exception e) {
+ logger.warn("Ignoring exception while closing {}", monitor, e);
+ }
+ }
+ }
+
+ public static class NetconfMonitoringOperationServiceFactory implements NetconfOperationServiceFactory {
+ private final NetconfMonitoringOperationService operationService;
+
+ public NetconfMonitoringOperationServiceFactory(NetconfMonitoringOperationService operationService) {
+ this.operationService = operationService;
+ }
+
+ @Override
+ public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ return operationService;
+ }
+ }
+}
--- /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.monitoring.osgi;
+
+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.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+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.controller.netconf.monitoring.Get;
+import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class NetconfMonitoringOperationService implements NetconfOperationService {
+
+ public static final HashSet<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
+
+ @Override
+ public String getCapabilityUri() {
+ return MonitoringConstants.URI;
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(MonitoringConstants.NAMESPACE);
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(MonitoringConstants.MODULE_NAME);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(MonitoringConstants.MODULE_REVISION);
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<List<String>> getLocation() {
+ return Optional.absent();
+ }
+ });
+
+ private final NetconfMonitoringService monitor;
+
+ public NetconfMonitoringOperationService(NetconfMonitoringService monitor) {
+ this.monitor = monitor;
+ }
+
+ private static String readSchema() {
+ String schemaLocation = "/META-INF/yang/ietf-netconf-monitoring.yang";
+ URL resource = Schemas.class.getClassLoader().getResource(schemaLocation);
+ Preconditions.checkNotNull(resource, "Unable to read schema content from %s", schemaLocation);
+ File file = new File(resource.getFile());
+ try {
+ return Files.toString(file, Charsets.UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to load schema from " + schemaLocation, e);
+ }
+ }
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return CAPABILITIES;
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<NetconfOperationFilter> getFilters() {
+ return Sets.<NetconfOperationFilter>newHashSet(new Get(monitor));
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.monitoring.osgi;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+public class NetconfMonitoringServiceTracker extends ServiceTracker<NetconfMonitoringService, NetconfMonitoringService> {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfMonitoringServiceTracker.class);
+
+ private ServiceRegistration<NetconfOperationServiceFactory> reg;
+
+ NetconfMonitoringServiceTracker(BundleContext context) {
+ super(context, NetconfMonitoringService.class, null);
+ }
+
+ @Override
+ public NetconfMonitoringService addingService(ServiceReference<NetconfMonitoringService> reference) {
+ Preconditions.checkState(reg == null, "Monitoring service was already added");
+
+ NetconfMonitoringService netconfMonitoringService = super.addingService(reference);
+
+ final NetconfMonitoringOperationService operationService = new NetconfMonitoringOperationService(
+ netconfMonitoringService);
+ NetconfOperationServiceFactory factory = new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
+ operationService);
+
+ Dictionary<String, ?> props = new Hashtable<>();
+ reg = context.registerService(NetconfOperationServiceFactory.class, factory, props);
+
+ return netconfMonitoringService;
+ }
+
+ @Override
+ public void removedService(ServiceReference<NetconfMonitoringService> reference,
+ NetconfMonitoringService netconfMonitoringService) {
+ if(reg!=null) {
+ try {
+ reg.unregister();
+ } catch (Exception e) {
+ logger.warn("Ignoring exception while unregistering {}", reg, 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.monitoring.xml;
+
+import org.opendaylight.controller.netconf.monitoring.xml.model.NetconfState;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.transform.dom.DOMResult;
+
+public class JaxBSerializer {
+
+ public Element toXml(NetconfState monitoringModel) {
+ DOMResult res = null;
+ try {
+ JAXBContext jaxbContext = JAXBContext.newInstance(NetconfState.class);
+ Marshaller marshaller = jaxbContext.createMarshaller();
+
+ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
+
+ res = new DOMResult();
+ marshaller.marshal(monitoringModel, res);
+ } catch (JAXBException e) {
+ throw new RuntimeException("Unable to serialize netconf state " + monitoringModel, e);
+ }
+ return ((Document)res.getNode()).getDocumentElement();
+ }
+}
--- /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.monitoring.xml.model;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Yang;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+
+import javax.annotation.Nullable;
+import javax.xml.bind.annotation.XmlElement;
+import java.util.Collection;
+
+final class MonitoringSchema {
+
+ private final Schema schema;
+
+ public MonitoringSchema(Schema schema) {
+ this.schema = schema;
+ }
+
+ @XmlElement(name = "identifier")
+ public String getIdentifier() {
+ return schema.getIdentifier();
+ }
+
+ @XmlElement(name = "namespace")
+ public String getNamespace() {
+ return schema.getNamespace().getValue().toString();
+ }
+
+ @XmlElement(name = "location")
+ public Collection<String> getLocation() {
+ return Collections2.transform(schema.getLocation(), new Function<Schema.Location, String>() {
+ @Nullable
+ @Override
+ public String apply(@Nullable Schema.Location input) {
+ return input.getEnumeration().toString();
+ }
+ });
+ }
+
+ @XmlElement(name = "version")
+ public String getVersion() {
+ return schema.getVersion();
+ }
+
+ @XmlElement(name = "format")
+ public String getFormat() {
+ Preconditions.checkState(schema.getFormat() == Yang.class, "Only yang format permitted, but was %s", schema.getFormat());
+ return "yang";
+ }
+}
--- /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.monitoring.xml.model;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+final class MonitoringSession {
+
+ @XmlTransient
+ private Session managementSession;
+
+ public MonitoringSession(Session managementSession) {
+ this.managementSession = managementSession;
+ }
+
+ public MonitoringSession() {
+ }
+
+ public void setManagementSession(Session managementSession) {
+ this.managementSession = managementSession;
+ }
+
+ @XmlElement(name = "session-id")
+ public long getId() {
+ return managementSession.getSessionId();
+ }
+
+ @XmlElement(name = "source-host")
+ public String getSourceHost() {
+ return managementSession.getSourceHost().getDomainName().getValue();
+ }
+
+ @XmlElement(name = "login-time")
+ public String getLoginTime() {
+ return managementSession.getLoginTime().getValue();
+ }
+
+ @XmlElement(name = "in-bad-rpcs")
+ public Long getInBadRpcs() {
+ return managementSession.getInBadRpcs().getValue();
+ }
+
+ @XmlElement(name = "in-rpcs")
+ public Long getInRpcs() {
+ return managementSession.getInRpcs().getValue();
+ }
+
+ @XmlElement(name = "out-notifications")
+ public Long getOutNotifications() {
+ return managementSession.getOutNotifications().getValue();
+ }
+
+ @XmlElement(name = "out-rpc-errors")
+ public Long getOutRpcErrors() {
+ return managementSession.getOutRpcErrors().getValue();
+ }
+
+ @XmlElement(name = "transport")
+ public String getTransport() {
+ Preconditions.checkState(managementSession.getTransport() == NetconfSsh.class);
+ return "netconf-ssh";
+ }
+
+ @XmlElement(name = "username")
+ public String getUsername() {
+ return managementSession.getUsername();
+ }
+}
--- /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.monitoring.xml.model;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+
+import javax.annotation.Nullable;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.Collection;
+
+@XmlRootElement(name = MonitoringConstants.NETCONF_MONITORING_XML_ROOT_ELEMENT)
+public final class NetconfState {
+
+ private Schemas schemas;
+ private Sessions sessions;
+
+ public NetconfState(NetconfMonitoringService monitoringService) {
+ this.sessions = monitoringService.getSessions();
+ this.schemas = monitoringService.getSchemas();
+ }
+
+ public NetconfState() {
+ }
+
+
+
+ @XmlElementWrapper(name="schemas")
+ @XmlElement(name="schema")
+ public Collection<MonitoringSchema> getSchemas() {
+ return Collections2.transform(schemas.getSchema(), new Function<Schema, MonitoringSchema>() {
+ @Nullable
+ @Override
+ public MonitoringSchema apply(@Nullable Schema input) {
+ return new MonitoringSchema(input);
+ }
+ });
+ }
+
+ @XmlElementWrapper(name="sessions")
+ @XmlElement(name="session")
+ public Collection<MonitoringSession> getSessions() {
+ return Collections2.transform(sessions.getSession(), new Function<Session, MonitoringSession>() {
+ @Nullable
+ @Override
+ public MonitoringSession apply(@Nullable Session input) {
+ return new MonitoringSession(input);
+ }
+ });
+ }
+}
--- /dev/null
+@XmlSchema(
+ elementFormDefault = XmlNsForm.QUALIFIED,
+// xmlns = {
+// @XmlNs(namespaceURI = MonitoringConstants.NAMESPACE, prefix = "")
+// }
+ namespace = MonitoringConstants.NAMESPACE
+)
+package org.opendaylight.controller.netconf.monitoring.xml.model;
+
+import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
+
+import javax.xml.bind.annotation.XmlNsForm;
+import javax.xml.bind.annotation.XmlSchema;
\ 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.monitoring.xml;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.monitoring.xml.model.NetconfState;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SessionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.DateAndTime;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.ZeroBasedCounter32;
+import org.w3c.dom.Element;
+
+import java.util.Date;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class JaxBSerializerTest {
+
+ @Test
+ public void testName() throws Exception {
+
+ NetconfMonitoringService service = new NetconfMonitoringService() {
+
+ @Override
+ public Sessions getSessions() {
+ return new SessionsBuilder().setSession(Lists.newArrayList(getMockSession())).build();
+ }
+
+ @Override
+ public Schemas getSchemas() {
+ return new SchemasBuilder().setSchema(Lists.<Schema>newArrayList()).build();
+ }
+ };
+ NetconfState model = new NetconfState(service);
+ Element xml = new JaxBSerializer().toXml(model);
+ System.out.println(XmlUtil.toString(xml));
+ }
+
+ private Session getMockSession() {
+ Session mocked = mock(Session.class);
+ doReturn(1L).when(mocked).getSessionId();
+ doReturn(new DateAndTime(new Date().toString())).when(mocked).getLoginTime();
+ doReturn(new Host(new DomainName("address/port"))).when(mocked).getSourceHost();
+ doReturn(new ZeroBasedCounter32(0L)).when(mocked).getInBadRpcs();
+ doReturn(new ZeroBasedCounter32(0L)).when(mocked).getInRpcs();
+ doReturn(new ZeroBasedCounter32(0L)).when(mocked).getOutNotifications();
+ doReturn(new ZeroBasedCounter32(0L)).when(mocked).getOutRpcErrors();
+ doReturn(NetconfSsh.class).when(mocked).getTransport();
+ doReturn("username").when(mocked).getUsername();
+ return mocked;
+ }
+}
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.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
import org.opendaylight.controller.netconf.util.handler.FramingMechanismHandlerFactory;
import org.opendaylight.controller.netconf.util.handler.NetconfMessageAggregator;
channel.pipeline().addAfter("aggregator", "chunkDecoder", new NetconfMessageChunkDecoder());
}
changeState(State.ESTABLISHED);
- S session = getSession(sessionListener, channel, doc);
+ S session = getSession(sessionListener, channel, netconfMessage);
negotiationSuccessful(session);
} else {
final IllegalStateException cause = new IllegalStateException(
"Received message was not hello as expected, but was " + XmlUtil.toString(doc));
+ logger.warn("Negotiation of netconf session failed", cause);
negotiationFailed(cause);
}
}
- protected abstract S getSession(SessionListener sessionListener, Channel channel, Document doc);
+ protected abstract S getSession(SessionListener sessionListener, Channel channel, NetconfMessage message);
private boolean isHelloMessage(Document doc) {
try {
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import java.util.List;
/**
* NetconfMessageFactory for (de)serializing DOM documents.
@Override
public 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' });
+ logMessage(bytes);
+
+ String additionalHeader = null;
+
+ if (startsWithAdditionalHeader(bytes)) {
+ // Auth information containing username, ip address... extracted for monitoring
+ int endOfAuthHeader = getAdditionalHeaderEndIndex(bytes);
if (endOfAuthHeader > -1) {
+ byte[] additionalHeaderBytes = Arrays.copyOfRange(bytes, 0, endOfAuthHeader + 2);
+ additionalHeader = additionalHeaderToString(additionalHeaderBytes);
bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length);
}
}
- NetconfMessage message = null;
+ NetconfMessage message;
try {
Document doc = XmlUtil.readXmlToDocument(new ByteArrayInputStream(bytes));
- message = new NetconfMessage(doc);
+ message = new NetconfMessage(doc, additionalHeader);
} catch (final SAXException | IOException | IllegalStateException e) {
throw new NetconfDeserializerException("Could not parse message from " + new String(bytes), e);
}
return message;
}
+ private int getAdditionalHeaderEndIndex(byte[] bytes) {
+ for (String possibleEnd : Lists.newArrayList("]\n", "]\r\n")) {
+ int idx = ByteArray.findByteSequence(bytes, possibleEnd.getBytes(Charsets.UTF_8));
+
+ if (idx != -1) {
+ return idx;
+ }
+ }
+
+ return -1;
+ }
+
+ private boolean startsWithAdditionalHeader(byte[] bytes) {
+ List<String> possibleStarts = Lists.newArrayList("[", "\r\n[", "\n[");
+ for (String possibleStart : possibleStarts) {
+ int i = 0;
+ for (byte b : possibleStart.getBytes(Charsets.UTF_8)) {
+ if(bytes[i]!=b)
+ break;
+
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ private void logMessage(byte[] bytes) {
+ String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+ logger.debug("Parsing message \n{}", s);
+ }
+
+ private String additionalHeaderToString(byte[] bytes) {
+ return Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+ }
+
@Override
public byte[] put(NetconfMessage netconfMessage) {
if (clientId.isPresent()) {
import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
+import org.opendaylight.controller.netconf.api.NetconfSession;
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;
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";
+ public static final String GET = "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.util.messages;
+
+import com.google.common.io.Files;
+import org.junit.Test;
+
+import java.io.File;
+
+public class NetconfMessageFactoryTest {
+
+
+ @Test
+ public void testAuth() throws Exception {
+ NetconfMessageFactory parser = new NetconfMessageFactory();
+ File authHelloFile = new File(getClass().getResource("/netconfMessages/client_hello_with_auth.xml").getFile());
+ parser.parse(Files.toByteArray(authHelloFile));
+
+ }
+}
[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">
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
</capabilities>
-</hello>
+</hello>
\ No newline at end of file
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<name>ref_dep</name>
</testing-dep>
+
+ <testing-deps>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+ <name>ref_dep</name>
+ </testing-deps>
+ <testing-deps>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+ <name>ref_dep_2</name>
+ </testing-deps>
</module>
<module>
<testing-dep>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
- <name>ref_dep</name>
+ <name>ref_dep_2</name>
</testing-dep>
</module>
</modules>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
<instance>
</instance>
<instance>
<name>ref_test1</name>
- <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ <provider>
+ /modules/module[type='impl-netconf'][name='test1']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
<instance>
<name>ref_dep_2</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ <provider>/modules/module[type='impl-dep'][name='dep2']
</provider>
</instance>
<instance>
<name>ref_test1</name>
- <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ <provider>
+ /modules/module[type='impl-netconf'][name='test1']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
<instance>
<name>ref_dep_2</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ <provider>/modules/module[type='impl-dep'][name='dep2']
</provider>
</instance>
<instance>
<name>ref_test1</name>
- <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ <provider>
+ /modules/module[type='impl-netconf'][name='test1']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
<instance>
<name>ref_dep_2</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ <provider>/modules/module[type='impl-dep'][name='dep2']
</provider>
</instance>
<instance>
<name>ref_test1</name>
- <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ <provider>
+ /modules/module[type='impl-netconf'][name='test1']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
<instance>
<name>ref_dep_2</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ <provider>/modules/module[type='impl-dep'][name='dep2']
</provider>
</instance>
<instance>
<name>ref_test1</name>
- <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ <provider>
+ /modules/module[type='impl-netconf'][name='test1']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
<instance>
<name>ref_dep_2</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ <provider>/modules/module[type='impl-dep'][name='dep2']
</provider>
</instance>
<instance>
<name>ref_test1</name>
- <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ <provider>
+ /modules/module[type='impl-netconf'][name='test1']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
</service>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
</service>
<instance>
<name>ref_dep</name>
<unknownAttribute>error</unknownAttribute>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
<instance>
<name>ref_dep_2</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ <provider>/modules/module[type='impl-dep'][name='dep2']
</provider>
</instance>
<instance>
<name>ref_test1</name>
- <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ <provider>
+ /modules/module[type='impl-netconf'][name='test1']
</provider>
</instance>
</service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
<instance>
<name>ref_dep</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+ <provider>/modules/module[type='impl-dep'][name='dep']
</provider>
</instance>
<instance>
<name>ref_dep_2</name>
- <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+ <provider>/modules/module[type='impl-dep'][name='dep2']
</provider>
</instance>
<instance>
<name>ref_test1</name>
- <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+ <provider>
+ /modules/module[type='impl-netconf'][name='test1']
</provider>
</instance>
</service>
<module>netconf-ssh</module>
<module>../../third-party/ganymed</module>
<module>../../third-party/com.siemens.ct.exi</module>
+ <module>netconf-monitoring</module>
+ <module>ietf-netconf-monitoring</module>
</modules>
<profiles>
<artifactId>netconf-impl</artifactId>
<version>${netconf.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-api</artifactId>