<artifactId>config-persister-api</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-file-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-directory-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-autodetect-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>configuration</artifactId>
<version>${configuration.version}</version>
</dependency>
-
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <version>1.50</version>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <version>1.50</version>
+ </dependency>
</dependencies>
</dependencyManagement>
});
b.childOption(ChannelOption.SO_KEEPALIVE, true);
+ customizeBootstrap(b);
+
// Bind and start to accept incoming connections.
final ChannelFuture f = b.bind(address);
LOG.debug("Initiated server {} at {}.", f, address);
return f;
+ }
+ /**
+ * Customize a server bootstrap before the server is created. This allows
+ * subclasses to assign non-default server options before the server is
+ * created.
+ *
+ * @param b Server bootstrap
+ */
+ protected void customizeBootstrap(final ServerBootstrap b) {
+ // The default is a no-op
}
/**
initializer.initializeChannel(ch, p);
}
});
+
+ customizeBootstrap(b);
+
p.connect();
LOG.debug("Client created.");
return p;
}
+ /**
+ * Customize a client bootstrap before the connection is attempted. This
+ * allows subclasses to assign non-default options before the client is
+ * created.
+ *
+ * @param b Client bootstrap
+ */
+ protected void customizeBootstrap(final Bootstrap b) {
+ // The default is a no-op
+ }
+
/**
* Creates a client.
*
p.connect();
return p;
-
}
/**
--- /dev/null
+Use
+GROUP_ID=com.mycompany.app
+ARTIFACT_ID=my-app
+mvn archetype:generate -DgroupId=$GROUP_ID -DartifactId=$ARTIFACT_ID \
+ -DarchetypeArtifactId=config-module-archetype -DarchetypeGroupId=org.opendaylight.controller \
+ -DarchetypeVersion=0.2.5-SNAPSHOT
+
+Module name and prefix define yang module name and its java name prefix.
+For example when creating thread factory wrapper, yang name might be
+thread-factory
+and java prefix
+ThreadFactory
\ No newline at end of file
<defaultValue>2013-04-05</defaultValue>
</requiredProperty>
<requiredProperty key="config-api-version">
- <defaultValue>0.2.4-SNAPSHOT</defaultValue>
+ <defaultValue>0.2.5-SNAPSHOT</defaultValue>
</requiredProperty>
<requiredProperty key="yang-maven-plugin-version">
<defaultValue>0.6.2-SNAPSHOT</defaultValue>
<properties>
<jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
+ <salGeneratorPath>${project.build.directory}/generated-sources/sal</salGeneratorPath>
<config.version>${config-api-version}</config.version>
+ <yangtools.version>${yang-maven-plugin-version}</yangtools.version>
<maven.bundle.version>${maven-bundle-plugin-version}</maven.bundle.version>
+ <java.version.source>1.7</java.version.source>
+ <java.version.target>1.7</java.version.target>
+ <maven.compile.plugin.version>2.5.1</maven.compile.plugin.version>
</properties>
<dependencies>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yang-maven-plugin-version}</version>
+ <version>${yangtools.version}</version>
<executions>
<execution>
<id>config</id>
</namespaceToPackage1>
</additionalConfiguration>
</generator>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ ${salGeneratorPath}
+ </outputBaseDir>
+ </generator>
</codeGenerators>
<inspectDependencies>true</inspectDependencies>
</configuration>
<artifactId>yang-jmx-generator-plugin</artifactId>
<version>${config.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
</dependencies>
</plugin>
<configuration>
<instructions>
<Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
- <Export-Package>
- ${yang-namespace-mapping-to}.${module-name},
- </Export-Package>
- <Import-Package>
- *
- </Import-Package>
</instructions>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>${maven.compile.plugin.version}</version>
+ <configuration>
+ <source>${java.version.source}</source>
+ <target>${java.version.target}</target>
+ <testSource>${java.version.source}</testSource>
+ <testTarget>${java.version.target}</testTarget>
+ </configuration>
+ </plugin>
</plugins>
</build>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>config-subsystem</artifactId>
- <groupId>org.opendaylight.controller</groupId>
- <version>0.2.5-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>config-persister-directory-adapter</artifactId>
- <name>${project.artifactId}</name>
- <packaging>bundle</packaging>
-
- <dependencies>
- <!-- compile dependencies -->
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- </dependency>
-
- <!-- test dependencies -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>mockito-configuration</artifactId>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
-
- </dependencies>
-
- <build>
- <plugins>
- <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
- <plugin>
- <groupId>org.codehaus.groovy.maven</groupId>
- <artifactId>gmaven-plugin</artifactId>
- <executions>
- <execution>
- <phase>generate-sources</phase>
- <goals>
- <goal>execute</goal>
- </goals>
- <configuration>
- <source>
- System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
- </source>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
- </Fragment-Host>
- <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
- </Provide-Capability>
- <Import-Package>
- com.google.common.base,
- com.google.common.io,
- org.apache.commons.io,
- org.opendaylight.controller.config.persist.api,
- org.slf4j
- </Import-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.config.persist.storage.directory;
-
-import com.google.common.base.Charsets;
-import com.google.common.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;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-public class DirectoryPersister implements Persister {
- private static final Logger logger = LoggerFactory.getLogger(DirectoryPersister.class);
- private static final Charset ENCODING = Charsets.UTF_8;
-
- public static final String MODULES_START = "//MODULES START";
- static final String SERVICES_START = "//SERVICES START";
- static final String CAPABILITIES_START = "//CAPABILITIES START";
-
-
- private final File storage;
- private static final String header, middle, footer;
-
- static {
- header = readResource("header.txt");
- middle = readResource("middle.txt");
- footer = readResource("footer.txt");
- }
-
- public DirectoryPersister(File storage) {
- checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
- this.storage = storage;
- }
-
- private static String readResource(String resource) {
- try {
- return IOUtils.toString(DirectoryPersister.class.getResourceAsStream("/" + resource));
- } catch (IOException e) {
- throw new IllegalStateException("Cannot load " + resource, e);
- }
- }
-
- @Override
- public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
- throw new UnsupportedOperationException("This adapter is read only. Please set readonly=true on " + getClass());
- }
-
- @Override
- public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- File[] filesArray = storage.listFiles();
- 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);
-
- List<ConfigSnapshotHolder> result = new ArrayList<>();
- for (File file : sortedFiles) {
- logger.trace("Adding file '{}' to combined result", file);
-
- ConfigSnapshotHolder configSnapshotHolder = loadLastConfig(file);
- result.add(configSnapshotHolder);
- }
- return result;
- }
-
- public static ConfigSnapshotHolder loadLastConfig(File file) throws IOException {
- final MyLineProcessor lineProcessor = new MyLineProcessor(file.getAbsolutePath());
- Files.readLines(file, ENCODING, lineProcessor);
- return lineProcessor.getConfigSnapshotHolder(header, middle, footer);
- }
-
-
- @Override
- public void close() {
-
- }
-
- @Override
- public String toString() {
- return "FileStorageAdapter [storage=" + storage + "]";
- }
-}
-
-class MyLineProcessor implements com.google.common.io.LineProcessor<String> {
- private final String fileNameForReporting;
-
- private boolean inModules, inServices, inCapabilities;
- private final StringBuffer modulesBuffer = new StringBuffer(), servicesBuilder = new StringBuffer();
- private final SortedSet<String> caps = new TreeSet<>();
-
- MyLineProcessor(String fileNameForReporting) {
- this.fileNameForReporting = fileNameForReporting;
- }
-
- @Override
- public String getResult() {
- return null;
- }
-
- @Override
- public boolean processLine(String line) throws IOException {
-
- String lineWithNewLine = line + System.lineSeparator();
- if (line.equals(DirectoryPersister.MODULES_START)) {
- checkState(inModules == false && inServices == false && inCapabilities == false);
- inModules = true;
- } else if (line.equals(DirectoryPersister.SERVICES_START)) {
- checkState(inModules == true && inServices == false && inCapabilities == false);
- inModules = false;
- inServices = true;
- } else if (line.equals(DirectoryPersister.CAPABILITIES_START)) {
- checkState(inModules == false && inServices == true && inCapabilities == false);
- inServices = false;
- inCapabilities = true;
- } else if (inModules) {
- modulesBuffer.append(lineWithNewLine);
- } else if (inServices) {
- servicesBuilder.append(lineWithNewLine);
- } else {
- caps.add(line);
- }
- return true;
- }
-
- private void checkFileConsistency(){
- checkState(inCapabilities, "File %s is missing delimiters in this order: %s", fileNameForReporting,
- Arrays.asList(DirectoryPersister.MODULES_START,
- DirectoryPersister.SERVICES_START,
- DirectoryPersister.CAPABILITIES_START));
- }
-
- String getModules() {
- checkFileConsistency();
- return modulesBuffer.toString();
- }
-
- String getServices() {
- checkFileConsistency();
- return servicesBuilder.toString();
- }
-
- SortedSet<String> getCapabilities() {
- checkFileConsistency();
- return caps;
- }
-
- ConfigSnapshotHolder getConfigSnapshotHolder(String header, String middle, String footer) {
- String combinedSnapshot = header + getModules() + middle + getServices() + footer;
- ConfigSnapshotHolder result = new ConfigSnapshotHolderImpl(combinedSnapshot, getCapabilities(), fileNameForReporting);
- return result;
- }
-
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.config.persist.storage.directory;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.api.PropertiesProvider;
-import org.opendaylight.controller.config.persist.api.StorageAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-
-/**
- * StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and
- * required capabilities will be merged together. Writing to this persister is not supported.
- */
-public class DirectoryStorageAdapter implements StorageAdapter {
- private static final Logger logger = LoggerFactory.getLogger(DirectoryStorageAdapter.class);
-
- public static final String DIRECTORY_STORAGE_PROP = "directoryStorage";
-
-
- @Override
- public Persister instantiate(PropertiesProvider propertiesProvider) {
- String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP);
- Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP));
- File storage = new File(fileStorageProperty);
- logger.debug("Using {}", storage);
- return new DirectoryPersister(storage);
- }
-
-}
+++ /dev/null
- </services>
-</data>
+++ /dev/null
-<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+++ /dev/null
- </modules>
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.config.persist.storage.directory;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.apache.commons.io.IOUtils;
-import org.junit.Test;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.test.PropertiesProviderTest;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class DirectoryStorageAdapterTest {
- Persister tested;
-
- private Persister instantiatePersisterFromAdapter(File file){
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",file.getPath());
- DirectoryStorageAdapter dsa = new DirectoryStorageAdapter();
- return dsa.instantiate(pp);
- }
- @Test
- public void testEmptyDirectory() throws Exception {
- File folder = new File("target/emptyFolder");
- folder.mkdir();
-
- tested = instantiatePersisterFromAdapter(folder);
- assertEquals(Collections.<ConfigSnapshotHolder>emptyList(), tested.loadLastConfigs());
-
- try {
- tested.persistConfig(new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- throw new RuntimeException();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- throw new RuntimeException();
- }
- });
- fail();
- } catch (UnsupportedOperationException e) {
-
- }
- }
-
- private File getFolder(String folderName) {
- File result = new File(("src/test/resources/" +
- folderName).replace("/", File.separator));
- assertTrue(result + " is not a directory", result.isDirectory());
- return result;
- }
-
- @Test
- public void testOneFile() throws Exception {
- File folder = getFolder("oneFile");
-
- tested = instantiatePersisterFromAdapter(folder);
-
- List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
- assertEquals(1, results.size());
- ConfigSnapshotHolder result = results.get(0);
- assertSnapshot(result, "oneFileExpected");
- }
-
-
- @Test
- public void testTwoFiles() throws Exception {
- File folder = getFolder("twoFiles");
- tested = instantiatePersisterFromAdapter(folder);
-
- 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"));
- expectedSnapshot = expectedSnapshot.replaceAll("\r","");
- String _snapshot = result.getConfigSnapshot();
- _snapshot = _snapshot.replaceAll("\r","");
- assertEquals(expectedCapabilities, result.getCapabilities());
- assertEquals(expectedSnapshot, _snapshot);
- }
-
-}
+++ /dev/null
-//MODULES START
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
- <name>yang-schema-service</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
- <name>hash-map-data-store</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
- <name>dom-broker</name>
- <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <name>ref_hash-map-data-store</name>
- </data-store>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
- <name>binding-broker-impl</name>
- <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <name>ref_binding-notification-broker</name>
- </notification-service>
- <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <name>ref_binding-data-broker</name>
- </data-broker>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
- <name>runtime-mapping-singleton</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
- <name>binding-notification-broker</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
- <name>binding-data-broker</name>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <name>ref_dom-broker</name>
- </dom-broker>
- <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
- <name>ref_runtime-mapping-singleton</name>
- </mapping-service>
- </module>
-//SERVICES START
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
- <instance>
- <name>ref_yang-schema-service</name>
- <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <instance>
- <name>ref_binding-notification-broker</name>
- <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <instance>
- <name>ref_hash-map-data-store</name>
- <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
- <instance>
- <name>ref_binding-broker-impl</name>
- <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
- <instance>
- <name>ref_runtime-mapping-singleton</name>
- <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <instance>
- <name>ref_dom-broker</name>
- <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <instance>
- <name>ref_binding-data-broker</name>
- <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
- </instance>
- </service>
-//CAPABILITIES START
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
+++ /dev/null
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
+++ /dev/null
-<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
- <name>yang-schema-service</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
- <name>hash-map-data-store</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
- <name>dom-broker</name>
- <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <name>ref_hash-map-data-store</name>
- </data-store>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
- <name>binding-broker-impl</name>
- <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <name>ref_binding-notification-broker</name>
- </notification-service>
- <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <name>ref_binding-data-broker</name>
- </data-broker>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
- <name>runtime-mapping-singleton</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
- <name>binding-notification-broker</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
- <name>binding-data-broker</name>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <name>ref_dom-broker</name>
- </dom-broker>
- <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
- <name>ref_runtime-mapping-singleton</name>
- </mapping-service>
- </module>
- </modules>
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
- <instance>
- <name>ref_yang-schema-service</name>
- <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <instance>
- <name>ref_binding-notification-broker</name>
- <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <instance>
- <name>ref_hash-map-data-store</name>
- <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
- <instance>
- <name>ref_binding-broker-impl</name>
- <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
- <instance>
- <name>ref_runtime-mapping-singleton</name>
- <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <instance>
- <name>ref_dom-broker</name>
- <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <instance>
- <name>ref_binding-data-broker</name>
- <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
- </instance>
- </service>
- </services>
-</data>
+++ /dev/null
-//MODULES START
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
- <name>yang-schema-service</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
- <name>hash-map-data-store</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
- <name>dom-broker</name>
- <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <name>ref_hash-map-data-store</name>
- </data-store>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
- <name>binding-broker-impl</name>
- <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <name>ref_binding-notification-broker</name>
- </notification-service>
- <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <name>ref_binding-data-broker</name>
- </data-broker>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
- <name>runtime-mapping-singleton</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
- <name>binding-notification-broker</name>
- </module>
-//SERVICES START
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
- <instance>
- <name>ref_yang-schema-service</name>
- <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <instance>
- <name>ref_binding-notification-broker</name>
- <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <instance>
- <name>ref_hash-map-data-store</name>
- <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
- <instance>
- <name>ref_binding-broker-impl</name>
- <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
- <instance>
- <name>ref_runtime-mapping-singleton</name>
- <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <instance>
- <name>ref_dom-broker</name>
- <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
- </instance>
- </service>
-//CAPABILITIES START
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
+++ /dev/null
-//MODULES START
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
- <name>binding-data-broker</name>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <name>ref_dom-broker</name>
- </dom-broker>
- <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
- <name>ref_runtime-mapping-singleton</name>
- </mapping-service>
- </module>
-//SERVICES START
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <instance>
- <name>ref_binding-data-broker</name>
- <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
- </instance>
- </service>
-//CAPABILITIES START
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
+++ /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>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>config-subsystem</artifactId>
- <groupId>org.opendaylight.controller</groupId>
- <version>0.2.5-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>config-persister-directory-autodetect-adapter</artifactId>
- <name>${project.artifactId}</name>
- <packaging>bundle</packaging>
-
- <dependencies>
- <!-- compile dependencies -->
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-adapter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-xml-adapter</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- </dependency>
-
- <!-- test dependencies -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>mockito-configuration</artifactId>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
-
- </dependencies>
-
- <build>
- <plugins>
- <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
- <plugin>
- <groupId>org.codehaus.groovy.maven</groupId>
- <artifactId>gmaven-plugin</artifactId>
- <executions>
- <execution>
- <phase>generate-sources</phase>
- <goals>
- <goal>execute</goal>
- </goals>
- <configuration>
- <source>
- System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
- </source>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
- </Fragment-Host>
- <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
- </Provide-Capability>
- <Import-Package>
- com.google.common.base,
- com.google.common.io,
- org.apache.commons.io,
- org.opendaylight.controller.config.persist.api,
- org.slf4j,
- com.google.common.collect,
- javax.xml.bind,
- javax.xml.bind.annotation,
- javax.xml.transform,
- javax.xml.transform.stream,
- org.eclipse.persistence.jaxb,
- org.apache.commons.lang3
- </Import-Package>
- <Private-Package>
- org.opendaylight.controller.config.persist.storage.file.xml.model,
- org.opendaylight.controller.config.persist.storage.directory,
- org.opendaylight.controller.config.persist.storage.directory.xml,
- </Private-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.config.persist.storage.directory.autodetect;
-
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.storage.directory.DirectoryPersister;
-import org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryPersister;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.xml.bind.JAXBException;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-public class AutodetectDirectoryPersister implements Persister {
- private static final Logger logger = LoggerFactory.getLogger(AutodetectDirectoryPersister.class);
-
- private final File storage;
-
- public AutodetectDirectoryPersister(File storage) {
- checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
- this.storage = storage;
- }
-
- @Override
- public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
- throw new UnsupportedOperationException("This adapter is read only. Please set readonly=true on " + getClass());
- }
-
- @Override
- public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- File[] filesArray = storage.listFiles();
- 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);
-
- List<ConfigSnapshotHolder> result = new ArrayList<>();
- for (File file : sortedFiles) {
- logger.trace("Adding file '{}' to combined result", file);
-
- FileType fileType = FileType.getFileType(file);
- logger.trace("File '{}' detected as {} storage", file, fileType);
-
- ConfigSnapshotHolder snapshot = loadLastConfig(file, fileType);
- result.add(snapshot);
- }
- return result;
- }
-
- private ConfigSnapshotHolder loadLastConfig(File file, FileType fileType) throws IOException {
- switch (fileType) {
- case plaintext:
- logger.warn("Plaintext configuration files are deprecated, and will not be supported in future versions. " +
- "Use xml files instead");
- return DirectoryPersister.loadLastConfig(file);
- case xml:
- try {
- return XmlDirectoryPersister.loadLastConfig(file);
- } catch (JAXBException e) {
- logger.warn("Unable to restore configuration snapshot from {}", file, e);
- throw new IllegalStateException("Unable to restore configuration snapshot from " + file, e);
- }
- default:
- throw new IllegalStateException("Unknown storage type " + fileType);
- }
- }
-
- @Override
- public void close() {
-
- }
-
- @Override
- public String toString() {
- final StringBuffer sb = new StringBuffer("AutodetectDirectoryPersister{");
- sb.append("storage=").append(storage);
- sb.append('}');
- return sb.toString();
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.config.persist.storage.directory.autodetect;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.api.PropertiesProvider;
-import org.opendaylight.controller.config.persist.api.StorageAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-
-/**
- * StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and
- * required capabilities will be merged together. Writing to this persister is not supported.
- */
-public class AutodetectDirectoryStorageAdapter implements StorageAdapter {
- private static final Logger logger = LoggerFactory.getLogger(AutodetectDirectoryStorageAdapter.class);
-
- public static final String DIRECTORY_STORAGE_PROP = "directoryStorage";
-
-
- @Override
- public Persister instantiate(PropertiesProvider propertiesProvider) {
- String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP);
- Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP));
- File storage = new File(fileStorageProperty);
- logger.debug("Using {}", storage);
- return new AutodetectDirectoryPersister(storage);
- }
-
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.config.persist.storage.directory.autodetect;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-import org.apache.commons.lang3.StringUtils;
-import org.opendaylight.controller.config.persist.storage.directory.DirectoryPersister;
-import org.opendaylight.controller.config.persist.storage.file.xml.model.ConfigSnapshot;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-
-enum FileType {
-
- plaintext, xml;
-
- public static final String XML_STORAGE_FIRST_LINE = "<" + ConfigSnapshot.SNAPSHOT_ROOT_ELEMENT_NAME + ">";
- private static final String XML_FILE_DEFINITION_LINE = "<?xml";
-
- static FileType getFileType(File file) {
- String firstLine = readFirstLine(file);
- if(isPlaintextStorage(firstLine)) {
- return plaintext;
- } else if(isXmlStorage(firstLine))
- return xml;
-
- throw new IllegalArgumentException("File " + file + " is not of permitted storage type: " + Arrays.toString(FileType.values()));
- }
-
- private static boolean isXmlStorage(String firstLine) {
- boolean isXml = false;
- isXml |= firstLine.startsWith(XML_STORAGE_FIRST_LINE);
- isXml |= firstLine.startsWith(XML_FILE_DEFINITION_LINE);
- return isXml;
- }
-
- private static boolean isPlaintextStorage(String firstLine) {
- return firstLine.startsWith(DirectoryPersister.MODULES_START);
-
- }
-
- @VisibleForTesting
- static String readFirstLine(File file) {
- FirstLineReadingProcessor callback = new FirstLineReadingProcessor();
- try {
- return Files.readLines(file, Charsets.UTF_8, callback);
- } catch (IOException e) {
- throw new IllegalArgumentException("Unable to detect file type of file " + file, e);
- }
- }
-
-
- private static class FirstLineReadingProcessor implements com.google.common.io.LineProcessor<String> {
- private String firstNonBlankLine;
-
- @Override
- public boolean processLine(String line) throws IOException {
- if(isEmptyLine(line)) {
- return true;
- } else {
- firstNonBlankLine = line.trim();
- return false;
- }
- }
-
- private boolean isEmptyLine(String line) {
- return StringUtils.isBlank(line);
- }
-
- @Override
- public String getResult() {
- return firstNonBlankLine;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.config.persist.storage.directory.autodetect;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import junit.framework.Assert;
-import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.test.PropertiesProviderTest;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-public class AutodetectDirectoryPersisterTest {
-
- @Test
- public void testCombined() throws Exception {
- File resourcePath = FileTypeTest.getResourceAsFile("/combined/1controller.txt.config");
- File parentFile = resourcePath.getParentFile();
-
- AutodetectDirectoryStorageAdapter adapter = new AutodetectDirectoryStorageAdapter();
-
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",parentFile.getPath());
- AutodetectDirectoryPersister persister = (AutodetectDirectoryPersister) adapter.instantiate(pp);
- List<ConfigSnapshotHolder> configs = persister.loadLastConfigs();
-
- Assert.assertEquals(2, configs.size());
- String snapFromTxt = configs.get(0).getConfigSnapshot();
- org.junit.Assert.assertThat(snapFromTxt, JUnitMatchers.containsString("<config>txt</config>"));
- org.junit.Assert.assertThat(snapFromTxt, JUnitMatchers.containsString("<service>txt</service>"));
-
- String snapFromXml = configs.get(1).getConfigSnapshot();
- org.junit.Assert.assertThat(snapFromXml, JUnitMatchers.containsString("<config>xml</config>"));
-
- Assert.assertEquals(configs.get(0).getCapabilities(), configs.get(1).getCapabilities());
- }
-
- @Test
- public void testInvalidXml() throws Exception {
- File resourcePath = FileTypeTest.getResourceAsFile("/bad_controller.xml.config");
- File parentFile = resourcePath.getParentFile();
-
- AutodetectDirectoryStorageAdapter adapter = new AutodetectDirectoryStorageAdapter();
-
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",parentFile.getPath());
- AutodetectDirectoryPersister persister = (AutodetectDirectoryPersister) adapter.instantiate(pp);
- try {
- List<ConfigSnapshotHolder> configs = persister.loadLastConfigs();
- fail("An exception of type " + IllegalStateException.class + " was expected");
- } catch (IllegalStateException ise){
- String message = ise.getMessage();
- assertThat(message, JUnitMatchers.containsString("Unable to restore configuration snapshot from "));
- }
-
- }
-
- @Test
- public void testPersistConfig() throws Exception {
- File resourcePath = FileTypeTest.getResourceAsFile("/combined/1controller.txt.config");
- File parentFile = resourcePath.getParentFile();
-
- AutodetectDirectoryStorageAdapter adapter = new AutodetectDirectoryStorageAdapter();
-
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",parentFile.getPath());
- AutodetectDirectoryPersister persister = (AutodetectDirectoryPersister) adapter.instantiate(pp);
- List<ConfigSnapshotHolder> configs = null;
- try {
- configs = persister.loadLastConfigs();
- } catch (IOException e) {
- fail("An exception of type " + UnsupportedOperationException.class + " was expected");
- }
- Assert.assertEquals(2, configs.size());
- try {
- persister.persistConfig(configs.get(0));
- } catch (UnsupportedOperationException uoe){
- String message = uoe.getMessage();
- assertThat(message,JUnitMatchers.containsString("This adapter is read only. Please set readonly=true on class"));
- }
- }
-
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.config.persist.storage.directory.autodetect;
-
-import junit.framework.Assert;
-import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
-
-import java.io.File;
-
-public class FileTypeTest {
-
- @Test
- public void testPlaintext() throws Exception {
- File file = getResourceAsFile("/test.txt.config");
-
- FileType type = FileType.getFileType(file);
- Assert.assertEquals(FileType.plaintext, type);
-
- }
-
- @Test
- public void testXml() throws Exception {
- File file = getResourceAsFile("/test.xml.config");
-
- FileType type = FileType.getFileType(file);
- Assert.assertEquals(FileType.xml, type);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testUnknown() throws Exception {
- File file = getResourceAsFile("/unknown.config");
-
- try {
- FileType.getFileType(file);
- } catch (IllegalArgumentException e) {
- org.junit.Assert.assertThat(e.getMessage(), JUnitMatchers.containsString("File " + file + " is not of permitted storage type"));
- throw e;
- }
- }
-
- static File getResourceAsFile(String resource) {
- String f = FileTypeTest.class.getResource(resource).getFile();
- return new File(f);
- }
-
-}
+++ /dev/null
-<snapshot>
- <required-capabilities>
- <capability
- <capability>cap2</capability>
- <capability>capa a</capability>
- </required-capabilities>
- <configuration>
- <config>xml</config>
- </configuration>
-</snapshot>
\ No newline at end of file
+++ /dev/null
-//MODULES START
- <config>txt</config>
-//SERVICES START
- <service>txt</service>
-//CAPABILITIES START
-cap1&rev
-cap2
-capa a
\ No newline at end of file
+++ /dev/null
-<snapshot>
- <required-capabilities>
- <capability>cap1&rev</capability>
- <capability>cap2</capability>
- <capability>capa a</capability>
- </required-capabilities>
- <configuration>
- <config>xml</config>
- </configuration>
-</snapshot>
\ No newline at end of file
+++ /dev/null
-//MODULES START
-configuration
\ No newline at end of file
+++ /dev/null
-
-<snapshot>
\ No newline at end of file
+++ /dev/null
-unknown
-file type
\ No newline at end of file
*/
package org.opendaylight.controller.config.persist.storage.directory.xml;
+import com.google.common.base.Optional;
+import com.google.common.io.Files;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.storage.file.xml.model.ConfigSnapshot;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
+import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.SortedSet;
import static com.google.common.base.Preconditions.checkArgument;
private static final Logger logger = LoggerFactory.getLogger(XmlDirectoryPersister.class);
private final File storage;
+ private final Optional<FilenameFilter> extensionsFilter;
+ /**
+ * Creates XmlDirectoryPersister that picks up all files in specified folder
+ */
public XmlDirectoryPersister(File storage) {
+ this(storage, Optional.<FilenameFilter>absent());
+ }
+
+ /**
+ * Creates XmlDirectoryPersister that picks up files only with specified file extension
+ */
+ public XmlDirectoryPersister(File storage, Set<String> fileExtensions) {
+ this(storage, Optional.of(getFilter(fileExtensions)));
+ }
+
+ private XmlDirectoryPersister(File storage, Optional<FilenameFilter> extensionsFilter) {
checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
this.storage = storage;
+ this.extensionsFilter = extensionsFilter;
}
@Override
@Override
public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- File[] filesArray = storage.listFiles();
+ File[] filesArray = extensionsFilter.isPresent() ? storage.listFiles(extensionsFilter.get()) : storage.listFiles();
if (filesArray == null || filesArray.length == 0) {
return Collections.emptyList();
}
List<ConfigSnapshotHolder> result = new ArrayList<>();
for (File file : sortedFiles) {
logger.trace("Adding file '{}' to combined result", file);
- ConfigSnapshotHolder h = fromXmlSnapshot(file);
- result.add(h);
+ Optional<ConfigSnapshotHolder> h = fromXmlSnapshot(file);
+ // Ignore non valid snapshot
+ if(h.isPresent() == false) {
+ continue;
+ }
+
+ result.add(h.get());
}
return result;
}
- private ConfigSnapshotHolder fromXmlSnapshot(File file) {
+ private Optional<ConfigSnapshotHolder> fromXmlSnapshot(File file) {
try {
- return loadLastConfig(file);
+ return Optional.of(loadLastConfig(file));
} catch (JAXBException e) {
- logger.warn("Unable to restore configuration snapshot from {}", file, e);
- throw new IllegalStateException("Unable to restore configuration snapshot from " + file, e);
+ // In case of parse error, issue a warning, ignore and continue
+ logger.warn(
+ "Unable to parse configuration snapshot from {}. Initial config from {} will be IGNORED in this run. " +
+ "Note that subsequent config files may fail due to this problem. " +
+ "Xml markup in this file needs to be fixed, for detailed information see enclosed exception.",
+ file, file, e);
}
+
+ return Optional.absent();
}
public static ConfigSnapshotHolder loadLastConfig(File file) throws JAXBException {
};
}
+ private static FilenameFilter getFilter(final Set<String>fileExtensions) {
+ checkArgument(fileExtensions.isEmpty() == false, "No file extension provided", fileExtensions);
+
+ return new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ String ext = Files.getFileExtension(name);
+ return fileExtensions.contains(ext);
+ }
+ };
+ }
@Override
public void close() {
package org.opendaylight.controller.config.persist.storage.directory.xml;
import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Sets;
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.LoggerFactory;
import java.io.File;
+import java.util.Set;
/**
* StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and
private static final Logger logger = LoggerFactory.getLogger(XmlDirectoryStorageAdapter.class);
public static final String DIRECTORY_STORAGE_PROP = "directoryStorage";
+ public static final String INCLUDE_EXT_PROP = "includeExtensions";
+ private static final char EXTENSIONS_SEPARATOR = ',';
@Override
String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP);
Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP));
File storage = new File(fileStorageProperty);
- logger.debug("Using {}", storage);
- return new XmlDirectoryPersister(storage);
+ String fileExtensions = propertiesProvider.getProperty(INCLUDE_EXT_PROP);
+
+ logger.debug("Using storage: {}", storage);
+
+ if(fileExtensions != null) {
+ logger.debug("Using extensions: {}", fileExtensions);
+ return new XmlDirectoryPersister(storage, splitExtensions(fileExtensions));
+ } else {
+ return new XmlDirectoryPersister(storage);
+ }
+ }
+
+ private Set<String> splitExtensions(String fileExtensions) {
+ return Sets.newHashSet(Splitter.on(EXTENSIONS_SEPARATOR).trimResults().omitEmptyStrings()
+ .split(fileExtensions));
}
}
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
+
+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;
Persister tested;
Logger logger = LoggerFactory.getLogger(DirectoryStorageAdapterTest.class.toString());
- private Persister instantiatePersisterFromAdapter(File file){
+ private Persister instantiatePersisterFromAdapter(File file, Optional<String> extensions){
PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",file.getPath());
+ pp.addProperty(XmlDirectoryStorageAdapter.DIRECTORY_STORAGE_PROP,file.getPath());
+ if(extensions.isPresent()) {
+ pp.addProperty(XmlDirectoryStorageAdapter.INCLUDE_EXT_PROP, extensions.get());
+ }
+
XmlDirectoryStorageAdapter dsa = new XmlDirectoryStorageAdapter();
return dsa.instantiate(pp);
}
+ private Persister instantiatePersisterFromAdapter(File file){
+ return instantiatePersisterFromAdapter(file, Optional.<String>absent());
+ }
+
@Test
public void testEmptyDirectory() throws Exception {
File folder = new File("target/emptyFolder");
@Test
public void testOneFile() throws Exception {
File folder = getFolder("oneFile");
- tested = instantiatePersisterFromAdapter(folder);
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("xml"));
- logger.info("Testing : "+tested.toString());
+ logger.info("Testing : " + tested.toString());
List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
assertEquals(1, results.size());
ConfigSnapshotHolder result = results.get(0);
assertResult(result, "<config>1</config>", "cap1&rev", "cap2", "capa a");
}
+ @Test
+ public void testOneFileWrongExtension() throws Exception {
+ File folder = getFolder("oneFile");
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("aa, bb"));
+ logger.info("Testing : " + tested.toString());
+ }
+
private void assertResult(ConfigSnapshotHolder result, String s, String... caps) {
assertEquals(s, result.getConfigSnapshot().replaceAll("\\s", ""));
int i = 0;
}
@Test
- public void testTwoFiles() throws Exception {
+ public void testTwoFilesAllExtensions() throws Exception {
File folder = getFolder("twoFiles");
tested = instantiatePersisterFromAdapter(folder);
- logger.info("Testing : "+tested.toString());
+ logger.info("Testing : " + tested.toString());
List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
assertEquals(2, results.size());
assertResult(results.get(0), "<config>1</config>", "cap1-a", "cap2-a", "capa a-a");
assertResult(results.get(1), "<config>2</config>", "cap1-b", "cap2-b", "capa a-b");
+ }
+
+ @Test
+ public void testTwoFilesTwoExtensions() throws Exception {
+ File folder = getFolder("twoFiles");
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("xml, xml2"));
+ logger.info("Testing : " + tested.toString());
+ assertEquals(2, tested.loadLastConfigs().size());
+ }
+
+ @Test
+ public void testTwoFilesOnlyOneExtension() throws Exception {
+ File folder = getFolder("twoFiles");
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("xml"));
+ logger.info("Testing : " + tested.toString());
+ List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
+ assertEquals(1, results.size());
+ assertResult(results.get(0), "<config>1</config>", "cap1-a", "cap2-a", "capa a-a");
+ }
+
+ @Test
+ public void testTwoFilesOneInvalid() throws Exception {
+ File folder = getFolder("twoFiles_corrupt");
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("xml"));
+ logger.info("Testing : " + tested.toString());
+ List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
+ assertEquals(1, results.size());
+
+ assertResult(results.get(0), "<config>1</config>", "cap1-a", "cap2-a", "capa a-a");
}
}
--- /dev/null
+<snapshot>
+ <required-capabilities>
+ <capability>cap1-a</capability>
+ <capability>cap2-a</capability>
+ <capability>capa a-a</capability>
+ </required-capabilities>
+ <configuration>
+ <config>1</config>
+ </configuration>
+</snapshot>
\ No newline at end of file
--- /dev/null
+<snapshotInvalid>
+ <required-capabilities>
+ <capability>cap1-b</capability>
+ <capability>cap2-b</capability>
+ <capability>capa a-b</capability>
+ </required-capabilities>
+ <configuration>
+ <config>2</config>
+ </configuration>
+</snapshotInvalid>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>config-subsystem</artifactId>
- <groupId>org.opendaylight.controller</groupId>
- <version>0.2.5-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>config-persister-file-adapter</artifactId>
- <name>${project.artifactId}</name>
- <packaging>bundle</packaging>
-
- <dependencies>
- <!-- compile dependencies -->
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
-
- <!-- test dependencies -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>mockito-configuration</artifactId>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
- <plugin>
- <groupId>org.codehaus.groovy.maven</groupId>
- <artifactId>gmaven-plugin</artifactId>
- <executions>
- <execution>
- <phase>generate-sources</phase>
- <goals>
- <goal>execute</goal>
- </goals>
- <configuration>
- <source>
- System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
- </source>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
- </Fragment-Host>
- <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
- </Provide-Capability>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.config.persist.storage.file;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.io.Files;
-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;
-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;
-
-/**
- * StorageAdapter that stores configuration in a plain file.
- */
-public class FileStorageAdapter implements StorageAdapter, Persister {
- private static final Logger logger = LoggerFactory.getLogger(FileStorageAdapter.class);
-
-
- private static final Charset ENCODING = Charsets.UTF_8;
-
- public static final String FILE_STORAGE_PROP = "fileStorage";
- public static final String NUMBER_OF_BACKUPS = "numberOfBackups";
-
-
- private static final String SEPARATOR_E_PURE = "//END OF CONFIG";
- private static final String SEPARATOR_E = newLine(SEPARATOR_E_PURE);
-
- private static final String SEPARATOR_M_PURE = "//END OF SNAPSHOT";
- private static final String SEPARATOR_M = newLine(SEPARATOR_M_PURE);
-
- private static final String SEPARATOR_S = newLine("//START OF CONFIG");
-
- private static final String SEPARATOR_SL_PURE = "//START OF CONFIG-LAST";
- private static final String SEPARATOR_SL = newLine(SEPARATOR_SL_PURE);
-
- private static Integer numberOfStoredBackups;
- private File storage;
-
- @Override
- public Persister instantiate(PropertiesProvider propertiesProvider) {
- File storage = extractStorageFileFromProperties(propertiesProvider);
- logger.debug("Using file {}", storage.getAbsolutePath());
- // Create file if it does not exist
- File parentFile = storage.getAbsoluteFile().getParentFile();
- if (parentFile.exists() == false) {
- logger.debug("Creating parent folders {}", parentFile);
- parentFile.mkdirs();
- }
- if (storage.exists() == false) {
- logger.debug("Storage file does not exist, creating empty file");
- try {
- boolean result = storage.createNewFile();
- if (result == false)
- throw new RuntimeException("Unable to create storage file " + storage);
- } catch (IOException e) {
- throw new RuntimeException("Unable to create storage file " + storage, e);
- }
- }
- if (numberOfStoredBackups == 0) {
- throw new RuntimeException(NUMBER_OF_BACKUPS
- + " property should be either set to positive value, or ommited. Can not be set to 0.");
- }
- setFileStorage(storage);
- return this;
- }
-
- @VisibleForTesting
- void setFileStorage(File storage) {
- this.storage = storage;
- }
-
- @VisibleForTesting
- void setNumberOfBackups(Integer numberOfBackups) {
- numberOfStoredBackups = numberOfBackups;
- }
-
- private static File extractStorageFileFromProperties(PropertiesProvider propertiesProvider) {
- String fileStorageProperty = propertiesProvider.getProperty(FILE_STORAGE_PROP);
- Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(FILE_STORAGE_PROP));
- File result = new File(fileStorageProperty);
- String numberOfBAckupsAsString = propertiesProvider.getProperty(NUMBER_OF_BACKUPS);
- if (numberOfBAckupsAsString != null) {
- numberOfStoredBackups = Integer.valueOf(numberOfBAckupsAsString);
- } else {
- numberOfStoredBackups = Integer.MAX_VALUE;
- }
- logger.trace("Property {} set to {}", NUMBER_OF_BACKUPS, numberOfStoredBackups);
- return result;
- }
-
- private static String newLine(String string) {
- return string + "\n";
- }
-
- @Override
- public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
- Preconditions.checkNotNull(storage, "Storage file is null");
-
- String content = Files.toString(storage, ENCODING);
- if (numberOfStoredBackups == Integer.MAX_VALUE) {
- resetLastConfig(content);
- persistLastConfig(holder);
- } else {
- if (numberOfStoredBackups == 1) {
- Files.write("", storage, ENCODING);
- persistLastConfig(holder);
- } else {
- int count = StringUtils.countMatches(content, SEPARATOR_S);
- if ((count + 1) < numberOfStoredBackups) {
- resetLastConfig(content);
- persistLastConfig(holder);
- } else {
- String contentSubString = StringUtils.substringBefore(content, SEPARATOR_E);
- contentSubString = contentSubString.concat(SEPARATOR_E_PURE);
- content = StringUtils.substringAfter(content, contentSubString);
- resetLastConfig(content);
- persistLastConfig(holder);
- }
- }
- }
- }
-
- private void resetLastConfig(String content) throws IOException {
- content = content.replaceFirst(SEPARATOR_SL, SEPARATOR_S);
- Files.write(content, storage, ENCODING);
- }
-
- private void persistLastConfig(ConfigSnapshotHolder holder) throws IOException {
- Files.append(SEPARATOR_SL, storage, ENCODING);
- String snapshotAsString = holder.getConfigSnapshot();
- Files.append(newLine(snapshotAsString), storage, ENCODING);
- Files.append(SEPARATOR_M, storage, ENCODING);
- Files.append(toStringCaps(holder.getCapabilities()), storage, ENCODING);
- Files.append(SEPARATOR_E, storage, ENCODING);
- }
-
- private CharSequence toStringCaps(Set<String> capabilities) {
- StringBuilder b = new StringBuilder();
- for (String capability : capabilities) {
- b.append(newLine(capability));
- }
- return b.toString();
- }
-
- @Override
- public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- Preconditions.checkNotNull(storage, "Storage file is null");
-
- if (!storage.exists()) {
- return Collections.emptyList();
- }
-
- final LineProcessor lineProcessor = new LineProcessor();
- Files.readLines(storage, ENCODING, lineProcessor);
-
- 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> {
-
- private boolean inLastConfig, inLastSnapshot;
- private final StringBuffer snapshotBuffer = new StringBuffer();
- private final SortedSet<String> caps = new TreeSet<>();
-
- @Override
- public String getResult() {
- return null;
- }
-
- @Override
- public boolean processLine(String line) throws IOException {
- if (inLastConfig && line.equals(SEPARATOR_E_PURE)) {
- inLastConfig = false;
- return false;
- }
-
- if (inLastConfig && line.equals(SEPARATOR_M_PURE)) {
- inLastSnapshot = false;
- return true;
- }
-
- if (inLastConfig) {
- if (inLastSnapshot) {
- snapshotBuffer.append(line);
- snapshotBuffer.append(System.lineSeparator());
- } else {
- caps.add(line);
- }
- }
-
- if (line.equals(SEPARATOR_SL_PURE)) {
- inLastConfig = true;
- inLastSnapshot = true;
- }
-
- return true;
- }
-
- Optional<String> getConfigSnapshot() {
- final String xmlContent = snapshotBuffer.toString();
- if (xmlContent.equals("")) {
- return Optional.absent();
- } else {
- return Optional.of(xmlContent);
- }
- }
-
- SortedSet<String> getCapabilities() {
- return caps;
- }
-
- }
-
- @Override
- public void close() {
-
- }
-
- @Override
- public String toString() {
- return "FileStorageAdapter [storage=" + storage + "]";
- }
-
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.config.persist.storage.file;
-
-import com.google.common.base.Charsets;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-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 org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.test.PropertiesProviderTest;
-import static junit.framework.Assert.assertFalse;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-public class FileStorageAdapterTest {
-
- private static int i;
- private File file;
-
- @Before
- public void setUp() throws Exception {
- file = Files.createTempFile("testFilePersist", ".txt").toFile();
- if (!file.exists())
- return;
- com.google.common.io.Files.write("", file, Charsets.UTF_8);
- i = 1;
- }
-
-
- @Test
- public void testFileAdapterAsPersister() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("fileStorage",file.getPath());
- pp.addProperty("numberOfBackups",Integer.toString(Integer.MAX_VALUE));
-
- Persister configPersister = storage.instantiate(pp);
- final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return createConfig();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return createCaps();
- }
- };
- configPersister.persistConfig(holder);
-
- configPersister.persistConfig(holder);
-
- Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
- new Predicate<String>() {
-
- @Override
- public boolean apply(String input) {
- if (input.equals(""))
- return false;
- return true;
- }
- });
- assertEquals(14, readLines.size());
-
- List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
- assertEquals(1, lastConf.size());
- ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
- assertEquals("<config>2</config>",
- configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
- assertEquals(createCaps(), configSnapshotHolder.getCapabilities());
- }
- @Test
- public void testFileAdapter() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.setFileStorage(file);
- storage.setNumberOfBackups(Integer.MAX_VALUE);
- final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return createConfig();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return createCaps();
- }
- };
- storage.persistConfig(holder);
-
- storage.persistConfig(holder);
-
- Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
- new Predicate<String>() {
-
- @Override
- public boolean apply(String input) {
- if (input.equals(""))
- return false;
- return true;
- }
- });
- assertEquals(14, readLines.size());
-
- List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
- assertEquals(1, lastConf.size());
- ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
- assertEquals("<config>2</config>",
- configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
- assertEquals(createCaps(), configSnapshotHolder.getCapabilities());
- }
-
- private SortedSet<String> createCaps() {
- SortedSet<String> caps = new TreeSet<>();
-
- caps.add("cap1");
- caps.add("cap2");
- caps.add("capaaaa as dasfasdf s2");
- return caps;
- }
-
- @Test
- public void testFileAdapterOneBackup() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.setFileStorage(file);
- storage.setNumberOfBackups(1);
- final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return createConfig();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return createCaps();
- }
- };
- storage.persistConfig(holder);
-
- storage.persistConfig(holder);
-
- Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
- new Predicate<String>() {
-
- @Override
- public boolean apply(String input) {
- if (input.equals(""))
- return false;
- return true;
- }
- });
- assertEquals(7, readLines.size());
-
- List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
- assertEquals(1, lastConf.size());
- ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
- assertEquals("<config>2</config>",
- configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
- }
-
- @Test
- public void testFileAdapterOnlyTwoBackups() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.setFileStorage(file);
- storage.setNumberOfBackups(2);
- final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return createConfig();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return createCaps();
- }
- };
- storage.persistConfig(holder);
-
- storage.persistConfig(holder);
- storage.persistConfig(holder);
-
- Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
- new Predicate<String>() {
-
- @Override
- public boolean apply(String input) {
- if (input.equals(""))
- return false;
- return true;
- }
- });
-
- assertEquals(14, readLines.size());
-
- List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
- assertEquals(1, lastConf.size());
- ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
- assertEquals("<config>3</config>",
- configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
- assertFalse(readLines.contains(holder.getConfigSnapshot()));
- }
-
- @Test
- public void testNoLastConfig() throws Exception {
- File file = Files.createTempFile("testFilePersist", ".txt").toFile();
- if (!file.exists())
- return;
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.setFileStorage(file);
-
- List<ConfigSnapshotHolder> elementOptional = storage.loadLastConfigs();
- assertThat(elementOptional.size(), is(0));
- }
-
- @Test(expected = NullPointerException.class)
- public void testNoProperties() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.loadLastConfigs();
- }
-
- @Test(expected = NullPointerException.class)
- public void testNoProperties2() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.persistConfig(new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return Mockito.mock(String.class);
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return new TreeSet<>();
- }
- } );
- }
-
- static String createConfig() {
- return "<config>" + i++ + "</config>";
- }
-
-}
<module>config-plugin-parent</module>
<module>config-util</module>
<module>config-persister-api</module>
- <module>config-persister-file-adapter</module>
<module>config-persister-file-xml-adapter</module>
<module>yang-jmx-generator</module>
<module>yang-jmx-generator-plugin</module>
<module>netty-threadgroup-config</module>
<module>netty-event-executor-config</module>
<module>netty-timer-config</module>
- <module>config-persister-directory-adapter</module>
<module>config-persister-directory-xml-adapter</module>
- <module>config-persister-directory-autodetect-adapter</module>
<module>yang-test-plugin</module>
<module>shutdown-api</module>
<module>shutdown-impl</module>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-api</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-file-xml-adapter</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-adapter</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-directory-xml-adapter</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-autodetect-adapter</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>org.apache.xml.resolver</artifactId>
<version>1.2.0</version>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
<!-- threadpool -->
<dependency>
</directory>
<excludes>
<exclude>version.properties</exclude>
+ <exclude>configuration/config.ini</exclude>
</excludes>
<outputDirectory>
opendaylight/
<outputDirectory>opendaylight</outputDirectory>
<filtered>true</filtered>
</file>
+ <file>
+ <source>src/main/resources/configuration/config.ini</source>
+ <outputDirectory>opendaylight/configuration</outputDirectory>
+ <filtered>true</filtered>
+ </file>
</files>
</assembly>
+++ /dev/null
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEAuC9hbEacpewvylI0mwFwjy3Wou2hpr/ncN9BBiFDSaG5yW2k
-3Oy+SCAcFCL+ZKWb6cc6Ch4gUeCwyEHRojZguuhliKtak9YQf6qbvpPLe00842Lx
-iqNAGurMpzizCDsGFq8ChaAkBZQI3TvcHuPoSUWSMJ+K8xHpRyUdVr6g2yEjezKJ
-sTXBtWaeCCh6YUafFujuDJk7fvYcPW7Je5KRBBStIKvxcMW0zB+7eq04deTHwGbJ
-gGjKWilQ72hsDDP3Hbp5CJMAYg1r4GlCmFx3KyHRGztgWgNgaD7nNpKCkTLjtmA6
-b4x7TA+jrzZ6Af2z5TMrI4dv5w1SrxHaZ+ziLQIDAQABAoIBAHTndeGgq/rQf8De
-Do+4CTaHtK0zQSAyu/azbXUzlZ7drKuCEVs8VMY4wzmwwGEnkF+A2YDkgEUX5X0l
-8aYQ97KKoS9u+43MGCrAIhyDeGrpqlT1TzRcy+qJz53v6gq2U/X/3QztiQ+VV078
-mIluxNgE9XYxPaNsYfGLSCTv1+9c8y/hjGVX2kwFK+u4ut0ZZETggNa8UxfaHVDS
-fIJQX9Gm3J3GSUV30fDGMBIUW6ESLc2L8b7u8Mp9TRP39ZeQSuEUjBe8MYKv0Rel
-oEpjZvcnniMTpFbLpndBYn7/AoIiEBvtCN8faVTuRRcvvLcsRm09IctzKQYnMh6M
-6PLKV+ECgYEA8HFRYaKHUzxpzE/fyon82GQbzqFFY0/bbWrfWICMfNbIgshJUie6
-FmH5iUFMfeqaT7v557HFM0GB9FeIeSbvd88YmiBAcRopZ3DfMkDH+DT73yJ+/TKG
-2nrQtdhyuTIs4bwHqeS2BBJYs7PK9R2rratF3l34Tf7mjlvyOgygHdUCgYEAxBo2
-8hEBlAVNcNb1hTYUxe1w1B6675/mFlmw98Xmj9dRYfICXNhahs8tX3/lsBEd+vBu
-fI0oyHaff8m5bPgGzD1ZMybfeROujNrgxaKVk7Ef0FDRRCop4bm18OroFlFAt9l8
-wMp++ToACbdvQvL/mjWMPYlIxhB/YxHswICZZvkCgYAexxKYwdo6sGAGlC7cWT9x
-X5cjowcjyEQZRHXkeUgCbufpvcOM7aLnXJE5nY8yCwbHsBM0MlBA2GDPKylAANjk
-aDEJAZneIHAuWodngl1Wi0m2bU7+ECqs6s2uiU9eH2sZVh1RBQK7kLGkBx6ys6KX
-L3ZZGYRAT6GplWFzRsx0JQKBgCeVlxPD5QqpC1nEumi6YvUVGdpnnZpzL3HBhxxs
-wT612wKnZFyze4qM1X7ahVXGDsQxtkvD/sCAWW/lG13orw6ZL6FIroF1PJ3ILOkY
-CZN3hJF7TtKwpCWhZB2OfWzL2AGEkE8mUP0j/Q/5DCd6f6f0OSvOw3bfq6cm3iB5
-lP2ZAoGAXsRN5TZTX4AQ2xTlrDQ8A5XgcvyWQpJOmEXMTyHV7VaJVzmNWFVAvndK
-5UIq8ALDwB2t7vjmMUW6euvIwqtXiop7G79UOb3e3NhzeyWFGQyBLqCRznGaXQTT
-dlFy73xhukZMhFnj006bjKCYvOPnwuGl3+0fuWil5Rq3jOuY5c8=
------END RSA PRIVATE KEY-----
reference\:file\:../lib/slf4j-api-1.7.2.jar@1:start,\
reference\:file\:../lib/logback-classic-1.0.9.jar@1:start,\
reference\:file\:../lib/logback-core-1.0.9.jar@1:start,\
- reference\:file\:../lib/logging.bridge-0.4.2-SNAPSHOT@1:start,\
+ reference\:file\:../lib/logging.bridge-${controller.version}@1:start,\
reference\:file\:../lib/jersey-core-1.17.jar@2:start,\
reference\:file\:../lib/jersey-server-1.17.jar@2:start
netconf.config.persister.active=1,2
# read startup configuration
-#netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.DirectoryStorageAdapter
-#netconf.config.persister.1.properties.directoryStorage=configuration/initial/
-#netconf.config.persister.1.readonly=true
-
-netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.autodetect.AutodetectDirectoryStorageAdapter
+netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
netconf.config.persister.1.properties.directoryStorage=configuration/initial/
+# include only xml files, files with other extensions will be skipped, multiple extensions are permitted e.g. netconf.config.persister.1.properties.includeExtensions=xml,cfg,config
+netconf.config.persister.1.properties.includeExtensions=xml
netconf.config.persister.1.readonly=true
-#netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
-#netconf.config.persister.3.properties.directoryStorage=configuration/initialXml/
-#netconf.config.persister.3.readonly=true
-
-#netconf.config.persister.4.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
-#netconf.config.persister.4.properties.fileStorage=configuration/current/controller.currentconfig.txt
-#netconf.config.persister.4.properties.numberOfBackups=1
-
netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
netconf.config.persister.2.properties.fileStorage=configuration/current/controller.currentconfig.xml
netconf.config.persister.2.properties.numberOfBackups=1
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
+ <artifactId>config-persister-file-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
<dependency>
<artifactId>config-persister-impl</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-impl</artifactId>
public class BindingBrokerTestFactory {
- private static final ClassPool CLASS_POOL = new ClassPool();
+ private static final ClassPool CLASS_POOL = ClassPool.getDefault();
private boolean startWithParsedSchema = true;
private ExecutorService executor;
private ClassPool classPool;
return startWithParsedSchema;
}
- public void setStartWithParsedSchema(boolean startWithParsedSchema) {
+ public void setStartWithParsedSchema(final boolean startWithParsedSchema) {
this.startWithParsedSchema = startWithParsedSchema;
}
return executor;
}
- public void setExecutor(ExecutorService executor) {
+ public void setExecutor(final ExecutorService executor) {
this.executor = executor;
}
return classPool;
}
- public void setClassPool(ClassPool classPool) {
+ public void setClassPool(final ClassPool classPool) {
this.classPool = classPool;
}
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
-import javassist.ClassPool;
-
import org.junit.Test;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
ListeningExecutorService executor = MoreExecutors.sameThreadExecutor();
BindingBrokerTestFactory factory = new BindingBrokerTestFactory();
factory.setExecutor(executor);
- factory.setClassPool(new ClassPool());
factory.setStartWithParsedSchema(getStartWithSchema());
testContext = factory.getTestContext();
testContext.start();
this.broker = broker;
}
- public Iterable<DOMStoreThreePhaseCommitCohort> ready() {
+ public synchronized Iterable<DOMStoreThreePhaseCommitCohort> ready() {
checkState(cohorts == null, "Transaction was already marked as ready.");
ImmutableList.Builder<DOMStoreThreePhaseCommitCohort> cohortsBuilder = ImmutableList.builder();
for (DOMStoreWriteTransaction subTx : getSubtransactions()) {
class ChangeListenerNotifyTask implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(ChangeListenerNotifyTask.class);
- private final Iterable<DataChangeListenerRegistration<?>> listeners;
+ private final Iterable<? extends DataChangeListenerRegistration<?>> listeners;
private final AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> event;
- public ChangeListenerNotifyTask(final Iterable<DataChangeListenerRegistration<?>> listeners,
+ public ChangeListenerNotifyTask(final Iterable<? extends DataChangeListenerRegistration<?>> listeners,
final AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> event) {
this.listeners = listeners;
this.event = event;
import static com.google.common.base.Preconditions.checkState;
import static org.opendaylight.controller.md.sal.dom.store.impl.StoreUtils.increase;
+import java.util.Collections;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrationNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrationNode.DataChangeListenerRegistration;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
private final ListeningExecutorService executor;
private final String name;
private final AtomicLong txCounter = new AtomicLong(0);
-
- private DataAndMetadataSnapshot snapshot;
- private ModificationApplyOperation operationTree;
private final ListenerRegistrationNode listenerTree;
+ private final AtomicReference<DataAndMetadataSnapshot> snapshot;
-
+ private ModificationApplyOperation operationTree;
private SchemaContext schemaContext;
public InMemoryDOMDataStore(final String name, final ListeningExecutorService executor) {
this.name = Preconditions.checkNotNull(name);
this.executor = Preconditions.checkNotNull(executor);
- this.operationTree = new AlwaysFailOperation();
- this.snapshot = DataAndMetadataSnapshot.createEmpty();
this.listenerTree = ListenerRegistrationNode.createRoot();
+ this.snapshot = new AtomicReference<DataAndMetadataSnapshot>(DataAndMetadataSnapshot.createEmpty());
+ this.operationTree = new AlwaysFailOperation();
}
@Override
@Override
public DOMStoreReadTransaction newReadOnlyTransaction() {
- return new SnapshotBackedReadTransaction(nextIdentifier(), snapshot);
+ return new SnapshotBackedReadTransaction(nextIdentifier(), snapshot.get());
}
@Override
public DOMStoreReadWriteTransaction newReadWriteTransaction() {
- return new SnapshotBackedReadWriteTransaction(nextIdentifier(), snapshot, this, operationTree);
+ return new SnapshotBackedReadWriteTransaction(nextIdentifier(), snapshot.get(), this, operationTree);
}
@Override
public DOMStoreWriteTransaction newWriteOnlyTransaction() {
- return new SnaphostBackedWriteTransaction(nextIdentifier(), snapshot, this, operationTree);
+ return new SnaphostBackedWriteTransaction(nextIdentifier(), snapshot.get(), this, operationTree);
}
@Override
final InstanceIdentifier path, final L listener, final DataChangeScope scope) {
LOG.debug("{}: Registering data change listener {} for {}",name,listener,path);
ListenerRegistrationNode listenerNode = listenerTree;
- for(PathArgument arg :path.getPath()) {
+ for(PathArgument arg : path.getPath()) {
listenerNode = listenerNode.ensureChild(arg);
}
- synchronized (listener) {
- notifyInitialState(path, listener);
- }
- return listenerNode.registerDataChangeListener(path,listener, scope);
- }
- private void notifyInitialState(final InstanceIdentifier path,
- final AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> listener) {
- Optional<StoreMetadataNode> currentState = snapshot.read(path);
- try {
+ /*
+ * Make sure commit is not occurring right now. Listener has to be registered and its
+ * state capture enqueued at a consistent point.
+ *
+ * FIXME: improve this to read-write lock, such that multiple listener registrations
+ * can occur simultaneously
+ */
+ final DataChangeListenerRegistration<L> reg;
+ synchronized (this) {
+ reg = listenerNode.registerDataChangeListener(path, listener, scope);
+
+ Optional<StoreMetadataNode> currentState = snapshot.get().read(path);
if (currentState.isPresent()) {
- NormalizedNode<?, ?> data = currentState.get().getData();
- listener.onDataChanged(DOMImmutableDataChangeEvent.builder() //
+ final NormalizedNode<?, ?> data = currentState.get().getData();
+
+ final DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder() //
.setAfter(data) //
.addCreated(path, data) //
- .build() //
- );
+ .build();
+ executor.submit(new ChangeListenerNotifyTask(Collections.singletonList(reg), event));
}
- } catch (Exception e) {
- LOG.error("Unhandled exception encountered when invoking listener {}", listener, e);
}
+ return reg;
}
private synchronized DOMStoreThreePhaseCommitCohort submit(
return name + "-" + txCounter.getAndIncrement();
}
- private synchronized void commit(final DataAndMetadataSnapshot currentSnapshot,
+ private void commit(final DataAndMetadataSnapshot currentSnapshot,
final StoreMetadataNode newDataTree, final Iterable<ChangeListenerNotifyTask> listenerTasks) {
LOG.debug("Updating Store snaphot version: {} with version:{}",currentSnapshot.getMetadataTree().getSubtreeVersion(),newDataTree.getSubtreeVersion());
if(LOG.isTraceEnabled()) {
LOG.trace("Data Tree is {}",StoreUtils.toStringTree(newDataTree));
}
- checkState(snapshot == currentSnapshot, "Store snapshot and transaction snapshot differs");
- snapshot = DataAndMetadataSnapshot.builder() //
+
+ final DataAndMetadataSnapshot newSnapshot = DataAndMetadataSnapshot.builder() //
.setMetadataTree(newDataTree) //
.setSchemaContext(schemaContext) //
.build();
- for(ChangeListenerNotifyTask task : listenerTasks) {
- executor.submit(task);
- }
+ /*
+ * The commit has to occur atomically with regard to listener registrations.
+ */
+ synchronized (this) {
+ final boolean success = snapshot.compareAndSet(currentSnapshot, newSnapshot);
+ checkState(success, "Store snapshot and transaction snapshot differ. This should never happen.");
+ for (ChangeListenerNotifyTask task : listenerTasks) {
+ executor.submit(task);
+ }
+ }
}
private static class SnapshotBackedReadTransaction implements DOMStoreReadTransaction {
@Override
public ListenableFuture<Boolean> canCommit() {
- final DataAndMetadataSnapshot snapshotCapture = snapshot;
+ final DataAndMetadataSnapshot snapshotCapture = snapshot.get();
final ModificationApplyOperation snapshotOperation = operationTree;
return executor.submit(new Callable<Boolean>() {
@Override
public ListenableFuture<Void> preCommit() {
- storeSnapshot = snapshot;
+ storeSnapshot = snapshot.get();
if(modification.getModificationType() == ModificationType.UNMODIFIED) {
return Futures.immediateFuture(null);
}
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Identifiable;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrationNode>, Identifiable<PathArgument> {
- private final Logger LOG = LoggerFactory.getLogger(ListenerRegistrationNode.class);
+ private static final Logger LOG = LoggerFactory.getLogger(ListenerRegistrationNode.class);
private final ListenerRegistrationNode parent;
private final Map<PathArgument, ListenerRegistrationNode> children;
@SuppressWarnings({ "rawtypes", "unchecked" })
public Collection<org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration<?>> getListeners() {
+ // FIXME: this is not thread-safe and races with listener (un)registration!
return (Collection) listeners;
}
* @param scope Scope of triggering event.
* @return
*/
- public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerDataChangeListener(final InstanceIdentifier path,
+ public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> DataChangeListenerRegistration<L> registerDataChangeListener(final InstanceIdentifier path,
final L listener, final DataChangeScope scope) {
DataChangeListenerRegistration<L> listenerReg = new DataChangeListenerRegistration<L>(path,listener, scope, this);
import static com.google.common.base.Preconditions.checkState;
+import java.util.LinkedHashMap;
import java.util.Map;
import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
import com.google.common.primitives.UnsignedLong;
public class StoreMetadataNode implements Immutable, Identifiable<PathArgument>, StoreTreeNode<StoreMetadataNode> {
private final Map<PathArgument, StoreMetadataNode> children;
+ /**
+ *
+ * @param data
+ * @param nodeVersion
+ * @param subtreeVersion
+ * @param children Map of children, must not be modified externally
+ */
protected StoreMetadataNode(final NormalizedNode<?, ?> data, final UnsignedLong nodeVersion,
final UnsignedLong subtreeVersion, final Map<PathArgument, StoreMetadataNode> children) {
this.nodeVersion = nodeVersion;
this.subtreeVersion = subtreeVersion;
this.data = data;
- this.children = ImmutableMap.copyOf(children);
-
+ this.children = Preconditions.checkNotNull(children);
}
public static Builder builder() {
}
public Iterable<StoreMetadataNode> getChildren() {
- return children.values();
+ return Iterables.unmodifiableIterable(children.values());
}
@Override
private UnsignedLong nodeVersion;
private UnsignedLong subtreeVersion;
private NormalizedNode<?, ?> data;
- private final ImmutableMap.Builder<PathArgument, StoreMetadataNode> children = ImmutableMap.builder();
+ private Map<PathArgument, StoreMetadataNode> children = new LinkedHashMap<>();
+ private boolean dirty = false;
private Builder() {}
}
public Builder add(final StoreMetadataNode node) {
+ if (dirty) {
+ children = new LinkedHashMap<>(children);
+ dirty = false;
+ }
children.put(node.getIdentifier(), node);
return this;
}
checkState(data != null, "Data node should not be null.");
checkState(subtreeVersion.compareTo(nodeVersion) >= 0,
"Subtree version must be equals or greater than node version.");
- return new StoreMetadataNode(data, nodeVersion, subtreeVersion, children.build());
+ dirty = true;
+ return new StoreMetadataNode(data, nodeVersion, subtreeVersion, children);
}
}
</plugins>
<pluginManagement>
<plugins>
- <!--This plugin's configuration is used to store Eclipse
+ <!--This plugin's configuration is used to store Eclipse
m2e settings only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-api</artifactId>
</dependency>
- <!--dependency> <groupId>org.opendaylight.yangtools</groupId> <artifactId>yang-data-impl</artifactId>
+ <!--dependency> <groupId>org.opendaylight.yangtools</groupId> <artifactId>yang-data-impl</artifactId>
<version>${yangtools.version}</version> </dependency -->
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
+ <artifactId>config-persister-file-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam</artifactId>
<version>${exam.version}</version>
- <!-- Compile scope here is intentional, it is used in TestHelper
- class which could be downloaded via nexus and reused in other integration
+ <!-- Compile scope here is intentional, it is used in TestHelper
+ class which could be downloaded via nexus and reused in other integration
tests. -->
<scope>compile</scope>
</dependency>
return callRpc(rpc, null)
}
- def resolveIdentifierInInvokeRpc(String identifier) {
+ private def resolveIdentifierInInvokeRpc(String identifier) {
if (identifier.indexOf("/") === -1) {
val identifierDecoded = identifier.urlPathArgDecode
val rpc = identifierDecoded.rpcDefinition
}
throw new ResponseException(NOT_FOUND, "RPC does not exist.");
}
- val slashErrorMsg = String.format("Identifier %n%s%ncan't contain slash character (/). +
- If slash is part of identifier name then use %2F placeholder.",identifier)
+ val slashErrorMsg = String.format(
+ "Identifier %n%s%ncan't contain slash character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier)
throw new ResponseException(NOT_FOUND, slashErrorMsg);
}
*/
package org.opendaylight.controller.sal.restconf.impl.test;
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.core.api.mount.MountService;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import static junit.framework.Assert.assertNotNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
public class RestGetOperationTest extends JerseyTest {
assertEquals(200, get(uri, MediaType.APPLICATION_XML));
}
+ /**
+ * MountPoint test. URI represents mount point.
+ *
+ * Slashes in URI behind mount point. lst1 element with key
+ * GigabitEthernet0%2F0%2F0%2F0 (GigabitEthernet0/0/0/0) is requested via
+ * GET HTTP operation. It is tested whether %2F character is replaced with
+ * simple / in InstanceIdentifier parameter in method
+ * {@link BrokerFacade#readConfigurationDataBehindMountPoint(MountInstance, InstanceIdentifier)}
+ * which is called in method {@link RestconfImpl#readConfigurationData}
+ *
+ *
+ * @throws ParseException
+ */
+ @Test
+ public void getDataWithSlashesBehindMountPoint() throws UnsupportedEncodingException, URISyntaxException,
+ ParseException {
+ InstanceIdentifier awaitedInstanceIdentifier = prepareInstanceIdentifierForList();
+ when(
+ brokerFacade.readConfigurationDataBehindMountPoint(any(MountInstance.class),
+ eq(awaitedInstanceIdentifier))).thenReturn(prepareCnDataForMountPointTest());
+ MountInstance mountInstance = mock(MountInstance.class);
+ when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
+ MountService mockMountService = mock(MountService.class);
+ when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
+
+ ControllerContext.getInstance().setMountService(mockMountService);
+
+ String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/lst1/GigabitEthernet0%2F0%2F0%2F0";
+ assertEquals(200, get(uri, MediaType.APPLICATION_XML));
+ }
+
+ private InstanceIdentifier prepareInstanceIdentifierForList() throws URISyntaxException, ParseException {
+ List<PathArgument> parameters = new ArrayList<>();
+
+ Date revision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-01-09");
+ URI uri = new URI("test:module");
+ QName qNameCont = QName.create(uri, revision, "cont");
+ QName qNameList = QName.create(uri, revision, "lst1");
+ QName qNameKeyList = QName.create(uri, revision, "lf11");
+
+ parameters.add(new InstanceIdentifier.NodeIdentifier(qNameCont));
+ parameters.add(new InstanceIdentifier.NodeIdentifierWithPredicates(qNameList, qNameKeyList,
+ "GigabitEthernet0/0/0/0"));
+ return new InstanceIdentifier(parameters);
+ }
+
@Test
public void getDataMountPointIntoHighestElement() throws UnsupportedEncodingException, URISyntaxException {
when(
response = target(uri).request("application/yang.api+xml").get();
validateModulesResponseXml(response);
}
+
// /streams/
@Test
public void getStreamsTest() throws UnsupportedEncodingException, FileNotFoundException {
assertTrue("Module2 in xml wasn't found", prepareXmlRegex("module2", "2014-01-02", "module:2", responseBody)
.find());
String[] split = responseBody.split("<module");
- assertEquals("<module element is returned more then once",2,split.length);
+ assertEquals("<module element is returned more then once", 2, split.length);
response = target(uri).request("application/yang.api+json").get();
assertEquals(200, response.getStatus());
assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody)
.find());
split = responseBody.split("\"module\"");
- assertEquals("\"module\" element is returned more then once",2,split.length);
+ assertEquals("\"module\" element is returned more then once", 2, split.length);
}
prepareJsonRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point",
responseBody).find());
String[] split = responseBody.split("\"module\"");
- assertEquals("\"module\" element is returned more then once",2,split.length);
-
+ assertEquals("\"module\" element is returned more then once", 2, split.length);
response = target(uri).request("application/yang.api+xml").get();
assertEquals(200, response.getStatus());
prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
.find());
split = responseBody.split("<module");
- assertEquals("<module element is returned more then once",2,split.length);
-
-
-
+ assertEquals("<module element is returned more then once", 2, split.length);
}
type string;
}
}
- }
+ list lst1 {
+ key "lf11";
+ leaf lf11 {
+ type string;
+ }
+ }
+ }
rpc rpc-test {
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.1-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+ <artifactId>l2switch-impl</artifactId>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.sample.l2switch.md.L2SwitchProvider</Bundle-Activator>
+ </instructions>
+ <manifestLocation>${project.build.directory}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+ <artifactId>l2switch-model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>net.sf.jung2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-topology</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md;
+
+import org.opendaylight.controller.sample.l2switch.md.addresstracker.AddressTracker;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterServiceImpl;
+import org.opendaylight.controller.sample.l2switch.md.inventory.InventoryService;
+import org.opendaylight.controller.sample.l2switch.md.packet.PacketHandler;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphDijkstra;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sample.l2switch.md.topology.TopologyLinkDataChangeHandler;
+import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * L2SwitchProvider serves as the Activator for our L2Switch OSGI bundle.
+ */
+public class L2SwitchProvider extends AbstractBindingAwareConsumer
+ implements AutoCloseable {
+
+ private final static Logger _logger = LoggerFactory.getLogger(L2SwitchProvider.class);
+
+ private Registration<NotificationListener> listenerRegistration;
+ private AddressTracker addressTracker;
+ private TopologyLinkDataChangeHandler topologyLinkDataChangeHandler;
+
+
+ /**
+ * Setup the L2Switch.
+ * @param consumerContext The context of the L2Switch.
+ */
+ @Override
+ public void onSessionInitialized(BindingAwareBroker.ConsumerContext consumerContext) {
+ DataBrokerService dataService = consumerContext.<DataBrokerService>getSALService(DataBrokerService.class);
+ addressTracker = new AddressTracker(dataService);
+
+ NetworkGraphService networkGraphService = new NetworkGraphDijkstra();
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataService, networkGraphService);
+
+ NotificationService notificationService =
+ consumerContext.<NotificationService>getSALService(NotificationService.class);
+ PacketProcessingService packetProcessingService =
+ consumerContext.<PacketProcessingService>getRpcService(PacketProcessingService.class);
+ PacketHandler packetHandler = new PacketHandler();
+ packetHandler.setAddressTracker(addressTracker);
+ packetHandler.setFlowWriterService(flowWriterService);
+ packetHandler.setPacketProcessingService(packetProcessingService);
+ packetHandler.setInventoryService(new InventoryService(dataService));
+
+ this.listenerRegistration = notificationService.registerNotificationListener(packetHandler);
+ this.topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataService, networkGraphService);
+ topologyLinkDataChangeHandler.registerAsDataChangeListener();
+ }
+
+ /**
+ * Cleanup the L2Switch.
+ * @throws Exception occurs when the NotificationListener is closed
+ */
+ @Override
+ public void close() throws Exception {
+ if (listenerRegistration != null)
+ listenerRegistration.close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.addresstracker;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.L2Addresses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2AddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2AddressKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Future;
+
+/**
+ * AddressTracker manages the MD-SAL data tree for L2Address (mac, node connector pairings) information.
+ */
+public class AddressTracker {
+
+ private final static Logger _logger = LoggerFactory.getLogger(AddressTracker.class);
+ private DataBrokerService dataService;
+
+ /**
+ * Construct an AddressTracker with the specified inputs
+ * @param dataService The DataBrokerService for the AddressTracker
+ */
+ public AddressTracker(DataBrokerService dataService) {
+ this.dataService = dataService;
+ }
+
+ /**
+ * Get all the L2 Addresses in the MD-SAL data tree
+ * @return All the L2 Addresses in the MD-SAL data tree
+ */
+ public L2Addresses getAddresses() {
+ return (L2Addresses)dataService.readOperationalData(InstanceIdentifier.<L2Addresses>builder(L2Addresses.class).toInstance());
+ }
+
+ /**
+ * Get a specific L2 Address in the MD-SAL data tree
+ * @param macAddress A MacAddress associated with an L2 Address object
+ * @return The L2 Address corresponding to the specified macAddress
+ */
+ public L2Address getAddress(MacAddress macAddress) {
+ return (L2Address) dataService.readOperationalData(createPath(macAddress));
+ }
+
+ /**
+ * Add L2 Address into the MD-SAL data tree
+ * @param macAddress The MacAddress of the new L2Address object
+ * @param nodeConnectorRef The NodeConnectorRef of the new L2Address object
+ * @return Future containing the result of the add operation
+ */
+ public Future<RpcResult<TransactionStatus>> addAddress(MacAddress macAddress, NodeConnectorRef nodeConnectorRef) {
+ if(macAddress == null || nodeConnectorRef == null) {
+ return null;
+ }
+
+ // Create L2Address
+ final L2AddressBuilder builder = new L2AddressBuilder();
+ builder.setKey(new L2AddressKey(macAddress))
+ .setMac(macAddress)
+ .setNodeConnectorRef(nodeConnectorRef);
+
+ // Add L2Address to MD-SAL data tree
+ final DataModificationTransaction it = dataService.beginTransaction();
+ it.putOperationalData(createPath(macAddress), builder.build());
+ return it.commit();
+ }
+
+ /**
+ * Remove L2Address from the MD-SAL data tree
+ * @param macAddress The MacAddress of an L2Address object
+ * @return Future containing the result of the remove operation
+ */
+ public Future<RpcResult<TransactionStatus>> removeHost(MacAddress macAddress) {
+ final DataModificationTransaction it = dataService.beginTransaction();
+ it.removeOperationalData(createPath(macAddress));
+ return it.commit();
+ }
+
+ /**
+ * Create InstanceIdentifier path for an L2Address in the MD-SAL data tree
+ * @param macAddress The MacAddress of an L2Address object
+ * @return InstanceIdentifier of the L2Address corresponding to the specified macAddress
+ */
+ private InstanceIdentifier<L2Address> createPath(MacAddress macAddress) {
+ return InstanceIdentifier.<L2Addresses>builder(L2Addresses.class)
+ .<L2Address, L2AddressKey>child(L2Address.class, new L2AddressKey(macAddress)).toInstance();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.flow;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+
+/**
+ * Service that adds packet forwarding flows to configuration data store.
+ */
+public interface FlowWriterService {
+
+ /**
+ * Writes a flow that forwards packets to destPort if destination mac in packet is destMac and
+ * source Mac in packet is sourceMac. If sourceMac is null then flow would not set any source mac,
+ * resulting in all packets with destMac being forwarded to destPort.
+ *
+ * @param sourceMac
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef);
+
+ /**
+ * Writes mac-to-mac flow on all ports that are in the path between given source and destination ports.
+ * It uses path provided by NetworkGraphService{@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService} to find a links{@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * between given ports. And then writes appropriate flow on each port that is covered in that path.
+ *
+ * @param sourceMac
+ * @param sourceNodeConnectorRef
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ public void addMacToMacFlowsUsingShortestPath(MacAddress sourceMac, NodeConnectorRef sourceNodeConnectorRef, MacAddress destMac, NodeConnectorRef destNodeConnectorRef);
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.flow;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+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.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Implementation of FlowWriterService{@link org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService},
+ * that builds required flow and writes to configuration data store using provided DataBrokerService
+ * {@link org.opendaylight.controller.sal.binding.api.data.DataBrokerService}
+ */
+public class FlowWriterServiceImpl implements FlowWriterService {
+ private static final Logger _logger = LoggerFactory.getLogger(FlowWriterServiceImpl.class);
+ private final DataBrokerService dataBrokerService;
+ private final NetworkGraphService networkGraphService;
+ private AtomicLong flowIdInc = new AtomicLong();
+ private AtomicLong flowCookieInc = new AtomicLong(0x2a00000000000000L);
+
+
+ public FlowWriterServiceImpl(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService) {
+ Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+ Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+ this.dataBrokerService = dataBrokerService;
+ this.networkGraphService = networkGraphService;
+ }
+
+ /**
+ * Writes a flow that forwards packets to destPort if destination mac in packet is destMac and
+ * source Mac in packet is sourceMac. If sourceMac is null then flow would not set any source mac,
+ * resulting in all packets with destMac being forwarded to destPort.
+ *
+ * @param sourceMac
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ @Override
+ public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef) {
+
+ Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
+ Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
+
+
+ // do not add flow if both macs are same.
+ if(sourceMac != null && destMac.equals(sourceMac)) {
+ _logger.info("In addMacToMacFlow: No flows added. Source and Destination mac are same.");
+ return;
+ }
+
+ // get flow table key
+ TableKey flowTableKey = new TableKey((short) 0); //TODO: Hard coded Table Id 0, need to get it from Configuration data.
+
+ //build a flow path based on node connector to program flow
+ InstanceIdentifier<Flow> flowPath = buildFlowPath(destNodeConnectorRef, flowTableKey);
+
+ // build a flow that target given mac id
+ Flow flowBody = createMacToMacFlow(flowTableKey.getId(), 0, sourceMac, destMac, destNodeConnectorRef);
+
+ // commit the flow in config data
+ writeFlowToConfigData(flowPath, flowBody);
+ }
+
+ /**
+ * Writes mac-to-mac flow on all ports that are in the path between given source and destination ports.
+ * It uses path provided by NetworkGraphService
+ * {@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService} to find a links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * between given ports. And then writes appropriate flow on each port that is covered in that path.
+ *
+ * @param sourceMac
+ * @param sourceNodeConnectorRef
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ @Override
+ public void addMacToMacFlowsUsingShortestPath(MacAddress sourceMac,
+ NodeConnectorRef sourceNodeConnectorRef,
+ MacAddress destMac,
+ NodeConnectorRef destNodeConnectorRef) {
+ Preconditions.checkNotNull(sourceMac, "Source mac address should not be null.");
+ Preconditions.checkNotNull(sourceNodeConnectorRef, "Source port should not be null.");
+ Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
+ Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
+
+ if(sourceNodeConnectorRef.equals(destNodeConnectorRef)) {
+ _logger.info("In addMacToMacFlowsUsingShortestPath: No flows added. Source and Destination ports are same.");
+ return;
+
+ }
+ NodeId sourceNodeId = new NodeId(sourceNodeConnectorRef.getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ NodeId destNodeId = new NodeId(destNodeConnectorRef.getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+
+ // add destMac-To-sourceMac flow on source port
+ addMacToMacFlow(destMac, sourceMac, sourceNodeConnectorRef);
+
+ // add sourceMac-To-destMac flow on destination port
+ addMacToMacFlow(sourceMac, destMac, destNodeConnectorRef);
+
+ if(!sourceNodeId.equals(destNodeId)) {
+ List<Link> linksInBeween = networkGraphService.getPath(sourceNodeId, destNodeId);
+
+ if(linksInBeween != null) {
+ // assumes the list order is maintained and starts with link that has source as source node
+ for(Link link : linksInBeween) {
+ // add sourceMac-To-destMac flow on source port
+ addMacToMacFlow(sourceMac, destMac, getSourceNodeConnectorRef(link));
+
+ // add destMac-To-sourceMac flow on destination port
+ addMacToMacFlow(destMac, sourceMac, getDestNodeConnectorRef(link));
+ }
+ }
+ }
+ }
+
+ private NodeConnectorRef getSourceNodeConnectorRef(Link link) {
+ InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+ = InstanceIdentifierUtils.createNodeConnectorIdentifier(
+ link.getSource().getSourceNode().getValue(),
+ link.getSource().getSourceTp().getValue());
+ return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+ }
+
+ private NodeConnectorRef getDestNodeConnectorRef(Link link) {
+ InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+ = InstanceIdentifierUtils.createNodeConnectorIdentifier(
+ link.getDestination().getDestNode().getValue(),
+ link.getDestination().getDestTp().getValue());
+
+ return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @return
+ */
+ private InstanceIdentifier<Flow> buildFlowPath(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) {
+
+ // generate unique flow key
+ FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
+ FlowKey flowKey = new FlowKey(flowId);
+
+ return InstanceIdentifierUtils.generateFlowInstanceIdentifier(nodeConnectorRef, flowTableKey, flowKey);
+ }
+
+ /**
+ * @param tableId
+ * @param priority
+ * @param sourceMac
+ * @param destMac
+ * @param destPort
+ * @return {@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder}
+ * builds flow that forwards all packets with destMac to given port
+ */
+ private Flow createMacToMacFlow(Short tableId, int priority,
+ MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destPort) {
+
+ // start building flow
+ FlowBuilder macToMacFlow = new FlowBuilder() //
+ .setTableId(tableId) //
+ .setFlowName("mac2mac");
+
+ // use its own hash code for id.
+ macToMacFlow.setId(new FlowId(Long.toString(macToMacFlow.hashCode())));
+
+ // create a match that has mac to mac ethernet match
+ EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder() //
+ .setEthernetDestination(new EthernetDestinationBuilder() //
+ .setAddress(destMac) //
+ .build());
+ // set source in the match only if present
+ if(sourceMac != null) {
+ ethernetMatchBuilder.setEthernetSource(new EthernetSourceBuilder()
+ .setAddress(sourceMac)
+ .build());
+ }
+ EthernetMatch ethernetMatch = ethernetMatchBuilder.build();
+ Match match = new MatchBuilder()
+ .setEthernetMatch(ethernetMatch)
+ .build();
+
+
+ Uri destPortUri = destPort.getValue().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
+
+ Action outputToControllerAction = new ActionBuilder() //
+ .setAction(new OutputActionCaseBuilder() //
+ .setOutputAction(new OutputActionBuilder() //
+ .setMaxLength(new Integer(0xffff)) //
+ .setOutputNodeConnector(destPortUri) //
+ .build()) //
+ .build()) //
+ .build();
+
+ // Create an Apply Action
+ ApplyActions applyActions = new ApplyActionsBuilder().setAction(ImmutableList.of(outputToControllerAction))
+ .build();
+
+ // Wrap our Apply Action in an Instruction
+ Instruction applyActionsInstruction = new InstructionBuilder() //
+ .setInstruction(new ApplyActionsCaseBuilder()//
+ .setApplyActions(applyActions) //
+ .build()) //
+ .build();
+
+ // Put our Instruction in a list of Instructions
+ macToMacFlow
+ .setMatch(match) //
+ .setInstructions(new InstructionsBuilder() //
+ .setInstruction(ImmutableList.of(applyActionsInstruction)) //
+ .build()) //
+ .setPriority(priority) //
+ .setBufferId(0L) //
+ .setHardTimeout(0) //
+ .setIdleTimeout(0) //
+ .setCookie(BigInteger.valueOf(flowCookieInc.getAndIncrement()))
+ .setFlags(new FlowModFlags(false, false, false, false, false));
+
+ return macToMacFlow.build();
+ }
+
+ /**
+ * Starts and commits data change transaction which
+ * modifies provided flow path with supplied body.
+ *
+ * @param flowPath
+ * @param flowBody
+ * @return transaction commit
+ */
+ private Future<RpcResult<TransactionStatus>> writeFlowToConfigData(InstanceIdentifier<Flow> flowPath,
+ Flow flowBody) {
+ DataModificationTransaction addFlowTransaction = dataBrokerService.beginTransaction();
+ addFlowTransaction.putConfigurationData(flowPath, flowBody);
+ return addFlowTransaction.commit();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.inventory;
+
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.*;
+
+/**
+ * InventoryService provides functions related to Nodes & NodeConnectors.
+ */
+public class InventoryService {
+ private DataBrokerService dataService;
+ // Key: SwitchId, Value: NodeConnectorRef that corresponds to NC between controller & switch
+ private HashMap<String, NodeConnectorRef> controllerSwitchConnectors;
+
+ /**
+ * Construct an InventoryService object with the specified inputs.
+ * @param dataService The DataBrokerService associated with the InventoryService.
+ */
+ public InventoryService(DataBrokerService dataService) {
+ this.dataService = dataService;
+ controllerSwitchConnectors = new HashMap<String, NodeConnectorRef>();
+ }
+
+ public HashMap<String, NodeConnectorRef> getControllerSwitchConnectors() {
+ return controllerSwitchConnectors;
+ }
+
+ // ToDo: Improve performance for thousands of switch ports
+ /**
+ * Get the External NodeConnectors of the network, which are the NodeConnectors connected to hosts.
+ * @return The list of external node connectors.
+ */
+ public List<NodeConnectorRef> getExternalNodeConnectors() {
+ // External NodeConnectors = All - Internal
+ ArrayList<NodeConnectorRef> externalNodeConnectors = new ArrayList<NodeConnectorRef>();
+ Set<String> internalNodeConnectors = new HashSet<>();
+
+ // Read Topology -- find list of switch-to-switch internal node connectors
+ NetworkTopology networkTopology =
+ (NetworkTopology)dataService.readOperationalData(
+ InstanceIdentifier.<NetworkTopology>builder(NetworkTopology.class).toInstance());
+
+ for (Topology topology : networkTopology.getTopology()) {
+ Topology completeTopology =
+ (Topology)dataService.readOperationalData(
+ InstanceIdentifierUtils.generateTopologyInstanceIdentifier(
+ topology.getTopologyId().getValue()));
+
+ for (Link link : completeTopology.getLink()) {
+ internalNodeConnectors.add(link.getDestination().getDestTp().getValue());
+ internalNodeConnectors.add(link.getSource().getSourceTp().getValue());
+ }
+ }
+
+ // Read Inventory -- contains list of all nodeConnectors
+ InstanceIdentifier.InstanceIdentifierBuilder<Nodes> nodesInsIdBuilder = InstanceIdentifier.<Nodes>builder(Nodes.class);
+ Nodes nodes = (Nodes)dataService.readOperationalData(nodesInsIdBuilder.toInstance());
+ if (nodes != null) {
+ for (Node node : nodes.getNode()) {
+ Node completeNode = (Node)dataService.readOperationalData(InstanceIdentifierUtils.createNodePath(node.getId()));
+ for (NodeConnector nodeConnector : completeNode.getNodeConnector()) {
+ // NodeConnector isn't switch-to-switch, so it must be controller-to-switch (internal) or external
+ if (!internalNodeConnectors.contains(nodeConnector.getId().getValue())) {
+ NodeConnectorRef ncRef = new NodeConnectorRef(
+ InstanceIdentifier.<Nodes>builder(Nodes.class).<Node, NodeKey>child(Node.class, node.getKey())
+ .<NodeConnector, NodeConnectorKey>child(NodeConnector.class, nodeConnector.getKey()).toInstance());
+
+ // External node connectors have "-" in their name for mininet, i.e. "s1-eth1"
+ if (nodeConnector.getAugmentation(FlowCapableNodeConnector.class).getName().contains("-")) {
+ externalNodeConnectors.add(ncRef);
+ }
+ // Controller-to-switch internal node connectors
+ else {
+ controllerSwitchConnectors.put(node.getId().getValue(), ncRef);
+ }
+ }
+ }
+ }
+ }
+
+ return externalNodeConnectors;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.packet;
+
+import org.opendaylight.controller.sample.l2switch.md.addresstracker.AddressTracker;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService;
+import org.opendaylight.controller.sample.l2switch.md.inventory.InventoryService;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.LLDP;
+import org.opendaylight.controller.sal.packet.LinkEncap;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * PacketHandler examines Ethernet packets to find L2Addresses (mac, nodeConnector) pairings
+ * of the sender and learns them.
+ * It also forwards the data packets appropriately dependending upon whether it knows about the
+ * target or not.
+ */
+public class PacketHandler implements PacketProcessingListener {
+
+ private final static Logger _logger = LoggerFactory.getLogger(PacketHandler.class);
+
+ private PacketProcessingService packetProcessingService;
+ private AddressTracker addressTracker;
+ private FlowWriterService flowWriterService;
+ private InventoryService inventoryService;
+
+ public void setAddressTracker(AddressTracker addressTracker) {
+ this.addressTracker = addressTracker;
+ }
+
+ public void setPacketProcessingService(PacketProcessingService packetProcessingService) {
+ this.packetProcessingService = packetProcessingService;
+ }
+
+ public void setFlowWriterService(FlowWriterService flowWriterService) {
+ this.flowWriterService = flowWriterService;
+ }
+
+ public void setInventoryService(InventoryService inventoryService) {
+ this.inventoryService = inventoryService;
+ }
+
+ /**
+ * The handler function for all incoming packets.
+ * @param packetReceived The incoming packet.
+ */
+ @Override
+ public void onPacketReceived(PacketReceived packetReceived) {
+
+ if(packetReceived == null) return;
+
+ try {
+ byte[] payload = packetReceived.getPayload();
+ RawPacket rawPacket = new RawPacket(payload);
+ NodeConnectorRef ingress = packetReceived.getIngress();
+
+ Packet packet = decodeDataPacket(rawPacket);
+
+ if(!(packet instanceof Ethernet)) return;
+
+ handleEthernetPacket(packet, ingress);
+
+ } catch(Throwable _e) {
+ _e.printStackTrace();
+ }
+ }
+
+ /**
+ * The handler function for Ethernet packets.
+ * @param packet The incoming Ethernet packet.
+ * @param ingress The NodeConnector where the Ethernet packet came from.
+ */
+ private void handleEthernetPacket(Packet packet, NodeConnectorRef ingress) {
+ byte[] srcMac = ((Ethernet) packet).getSourceMACAddress();
+ byte[] destMac = ((Ethernet) packet).getDestinationMACAddress();
+
+ if (srcMac == null || srcMac.length == 0) return;
+
+ Object enclosedPacket = packet.getPayload();
+
+ if (enclosedPacket instanceof LLDP)
+ return; // LLDP packets are handled by OpenFlowPlugin
+
+ // get l2address by src mac
+ // if unknown, add l2address
+ MacAddress srcMacAddress = toMacAddress(srcMac);
+ L2Address src = addressTracker.getAddress(srcMacAddress);
+ boolean isSrcKnown = (src != null);
+ if (!isSrcKnown) {
+ addressTracker.addAddress(srcMacAddress, ingress);
+ }
+
+ // get host by dest mac
+ // if known set dest known to true
+ MacAddress destMacAddress = toMacAddress(destMac);
+ L2Address dest = addressTracker.getAddress(destMacAddress);
+ boolean isDestKnown = (dest != null);
+
+ byte[] payload = packet.getRawPayload();
+ // if (src and dest known)
+ // sendpacket to dest and add src<->dest flow
+ if(isSrcKnown & isDestKnown) {
+ flowWriterService.addMacToMacFlowsUsingShortestPath(srcMacAddress, src.getNodeConnectorRef(),
+ destMacAddress, dest.getNodeConnectorRef());
+ sendPacketOut(payload, getControllerNodeConnector(dest.getNodeConnectorRef()), dest.getNodeConnectorRef());
+ } else {
+ // if (dest unknown)
+ // sendpacket to external links minus ingress
+ floodExternalPorts(payload, ingress);
+ }
+ }
+
+ /**
+ * Floods the specified payload on external ports, which are ports not connected to switches.
+ * @param payload The payload to be flooded.
+ * @param ingress The NodeConnector where the payload came from.
+ */
+ private void floodExternalPorts(byte[] payload, NodeConnectorRef ingress) {
+ List<NodeConnectorRef> externalPorts = inventoryService.getExternalNodeConnectors();
+ externalPorts.remove(ingress);
+
+ for (NodeConnectorRef egress : externalPorts) {
+ sendPacketOut(payload, getControllerNodeConnector(egress), egress);
+ }
+ }
+
+ /**
+ * Sends the specified packet on the specified port.
+ * @param payload The payload to be sent.
+ * @param ingress The NodeConnector where the payload came from.
+ * @param egress The NodeConnector where the payload will go.
+ */
+ private void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnectorRef egress) {
+ if (ingress == null || egress == null) return;
+ InstanceIdentifier<Node> egressNodePath = InstanceIdentifierUtils.getNodePath(egress.getValue());
+ TransmitPacketInput input = new TransmitPacketInputBuilder() //
+ .setPayload(payload) //
+ .setNode(new NodeRef(egressNodePath)) //
+ .setEgress(egress) //
+ .setIngress(ingress) //
+ .build();
+ packetProcessingService.transmitPacket(input);
+ }
+
+ /**
+ * Decodes an incoming packet.
+ * @param raw The raw packet to be decoded.
+ * @return The decoded form of the raw packet.
+ */
+ private Packet decodeDataPacket(RawPacket raw) {
+ if(raw == null) {
+ return null;
+ }
+ byte[] data = raw.getPacketData();
+ if(data.length <= 0) {
+ return null;
+ }
+ if(raw.getEncap().equals(LinkEncap.ETHERNET)) {
+ Ethernet res = new Ethernet();
+ try {
+ res.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
+ res.setRawPayload(raw.getPacketData());
+ } catch(Exception e) {
+ _logger.warn("Failed to decode packet: {}", e.getMessage());
+ }
+ return res;
+ }
+ return null;
+ }
+
+ /**
+ * Creates a MacAddress object out of a byte array.
+ * @param dataLinkAddress The byte-array form of a MacAddress
+ * @return MacAddress of the specified dataLinkAddress.
+ */
+ private MacAddress toMacAddress(byte[] dataLinkAddress) {
+ return new MacAddress(HexEncode.bytesToHexStringFormat(dataLinkAddress));
+ }
+
+ /**
+ * Gets the NodeConnector that connects the controller & switch for a specified switch port/node connector.
+ * @param nodeConnectorRef The nodeConnector of a switch.
+ * @return The NodeConnector that that connects the controller & switch.
+ */
+ private NodeConnectorRef getControllerNodeConnector(NodeConnectorRef nodeConnectorRef) {
+ NodeConnectorRef controllerSwitchNodeConnector = null;
+ HashMap<String, NodeConnectorRef> controllerSwitchConnectors = inventoryService.getControllerSwitchConnectors();
+ InstanceIdentifier<Node> nodePath = InstanceIdentifierUtils.getNodePath(nodeConnectorRef.getValue());
+ if (nodePath != null) {
+ NodeKey nodeKey = InstanceIdentifierUtils.getNodeKey(nodePath);
+ if (nodeKey != null) {
+ controllerSwitchNodeConnector = controllerSwitchConnectors.get(nodeKey.getId().getValue());
+ }
+ }
+ return controllerSwitchNodeConnector;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import com.google.common.base.Preconditions;
+import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
+import edu.uci.ics.jung.graph.DirectedSparseGraph;
+import edu.uci.ics.jung.graph.Graph;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Implementation of NetworkGraphService{@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService}.
+ * It uses Jung graph library internally to maintain a graph and optimum way to return shortest path using
+ * Dijkstra algorithm.
+ */
+public class NetworkGraphDijkstra implements NetworkGraphService {
+
+ private static final Logger _logger = LoggerFactory.getLogger(NetworkGraphDijkstra.class);
+
+ DijkstraShortestPath<NodeId, Link> shortestPath = null;
+ Graph<NodeId, Link> networkGraph = null;
+
+ /**
+ * Adds links to existing graph or creates new directed graph with given links if graph was not initialized.
+ * @param links
+ */
+ @Override
+ public synchronized void addLinks(List<Link> links) {
+ if(links == null || links.isEmpty()) {
+ _logger.info("In addLinks: No link added as links is null or empty.");
+ return;
+ }
+
+ if(networkGraph == null) {
+ networkGraph = new DirectedSparseGraph<>();
+ }
+
+ for(Link link : links) {
+ NodeId sourceNodeId = link.getSource().getSourceNode();
+ NodeId destinationNodeId = link.getDestination().getDestNode();
+ networkGraph.addVertex(sourceNodeId);
+ networkGraph.addVertex(destinationNodeId);
+ networkGraph.addEdge(link, sourceNodeId, destinationNodeId);
+ }
+ if(shortestPath == null) {
+ shortestPath = new DijkstraShortestPath<>(networkGraph);
+ } else {
+ shortestPath.reset();
+ }
+ }
+
+ /**
+ * removes links from existing graph.
+ * @param links
+ */
+ @Override
+ public synchronized void removeLinks(List<Link> links) {
+ Preconditions.checkNotNull(networkGraph, "Graph is not initialized, add links first.");
+
+ if(links == null || links.isEmpty()) {
+ _logger.info("In removeLinks: No link removed as links is null or empty.");
+ return;
+ }
+
+ for(Link link : links) {
+ networkGraph.removeEdge(link);
+ }
+
+ if(shortestPath == null) {
+ shortestPath = new DijkstraShortestPath<>(networkGraph);
+ } else {
+ shortestPath.reset();
+ }
+ }
+
+ /**
+ * returns a path between 2 nodes. Uses Dijkstra's algorithm to return shortest path.
+ * @param sourceNodeId
+ * @param destinationNodeId
+ * @return
+ */
+ @Override
+ public synchronized List<Link> getPath(NodeId sourceNodeId, NodeId destinationNodeId) {
+ Preconditions.checkNotNull(shortestPath, "Graph is not initialized, add links first.");
+
+ if(sourceNodeId == null || destinationNodeId == null) {
+ _logger.info("In getPath: returning null, as sourceNodeId or destinationNodeId is null.");
+ return null;
+ }
+
+ return shortestPath.getPath(sourceNodeId, destinationNodeId);
+ }
+
+ /**
+ * Clears the prebuilt graph, in case same service instance is required to process a new graph.
+ */
+ @Override
+ public synchronized void clear() {
+ networkGraph = null;
+ shortestPath = null;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+
+import java.util.List;
+
+/**
+ * Service that allows to build a network graph using Topology links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * and exposes operation that can be performed on such graph.
+ */
+public interface NetworkGraphService {
+
+ /**
+ * Adds links to existing graph or creates new graph with given links if graph was not initialized.
+ * @param links
+ */
+ public void addLinks(List<Link> links);
+
+ /**
+ * removes links from existing graph.
+ * @param links
+ */
+ public void removeLinks(List<Link> links);
+
+ /**
+ * returns a path between 2 nodes. Implementation should ideally return shortest path.
+ * @param sourceNodeId
+ * @param destinationNodeId
+ * @return
+ */
+ public List<Link> getPath(NodeId sourceNodeId, NodeId destinationNodeId);
+
+ /**
+ * Clears the prebuilt graph, in case same service instance is required to process a new graph.
+ */
+ public void clear();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Listens to data change events on topology links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * and maintains a topology graph using provided NetworkGraphService
+ * {@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService}.
+ * It refreshes the graph after a delay(default 10 sec) to accommodate burst of change events if they come in bulk.
+ * This is to avoid continuous refresh of graph on a series of change events in short time.
+ */
+public class TopologyLinkDataChangeHandler implements DataChangeListener {
+ private static final Logger _logger = LoggerFactory.getLogger(TopologyLinkDataChangeHandler.class);
+ private static final String DEFAULT_TOPOLOGY_ID = "flow:1";
+
+ private boolean networkGraphRefreshScheduled = false;
+ private final ScheduledExecutorService networkGraphRefreshScheduler = Executors.newScheduledThreadPool(1);
+ private final long DEFAULT_GRAPH_REFRESH_DELAY = 10;
+ private final long graphRefreshDelayInSec;
+
+ private final NetworkGraphService networkGraphService;
+ private final DataBrokerService dataBrokerService;
+
+ /**
+ * Uses default delay to refresh topology graph if this constructor is used.
+ * @param dataBrokerService
+ * @param networkGraphService
+ */
+ public TopologyLinkDataChangeHandler(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService) {
+ Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+ Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+ this.dataBrokerService = dataBrokerService;
+ this.networkGraphService = networkGraphService;
+ this.graphRefreshDelayInSec = DEFAULT_GRAPH_REFRESH_DELAY;
+ }
+
+ /**
+ *
+ * @param dataBrokerService
+ * @param networkGraphService
+ * @param graphRefreshDelayInSec
+ */
+ public TopologyLinkDataChangeHandler(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService,
+ long graphRefreshDelayInSec) {
+ Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+ Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+ this.dataBrokerService = dataBrokerService;
+ this.networkGraphService = networkGraphService;
+ this.graphRefreshDelayInSec = graphRefreshDelayInSec;
+ }
+
+ /**
+ * Based on if links have been added or removed in topology data store, schedules a refresh of network graph.
+ * @param dataChangeEvent
+ */
+ @Override
+ public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
+ if(dataChangeEvent == null) {
+ _logger.info("In onDataChanged: No Processing done as dataChangeEvent is null.");
+ }
+ Map<InstanceIdentifier<?>, DataObject> linkOriginalData = dataChangeEvent.getOriginalOperationalData();
+ Map<InstanceIdentifier<?>, DataObject> linkUpdatedData = dataChangeEvent.getUpdatedOperationalData();
+ // change this logic, once MD-SAL start populating DeletedOperationData Set
+ if(linkOriginalData != null && linkUpdatedData != null
+ && (linkOriginalData.size() != 0 || linkUpdatedData.size() != 0)
+ && !networkGraphRefreshScheduled) {
+ networkGraphRefreshScheduled = linkOriginalData.size() != linkUpdatedData.size();
+ if(networkGraphRefreshScheduled) {
+ networkGraphRefreshScheduler.schedule(new NetworkGraphRefresher(), graphRefreshDelayInSec, TimeUnit.SECONDS);
+ }
+ }
+
+ }
+
+ /**
+ * Registers as a data listener to receive changes done to
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * under {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology}
+ * operation data root.
+ */
+
+ public void registerAsDataChangeListener() {
+ InstanceIdentifier<Link> linkInstance = InstanceIdentifier.builder(NetworkTopology.class)
+ .child(Topology.class, new TopologyKey(new TopologyId(DEFAULT_TOPOLOGY_ID))).child(Link.class).toInstance();
+ dataBrokerService.registerDataChangeListener(linkInstance, this);
+ }
+
+ /**
+ *
+ */
+ private class NetworkGraphRefresher implements Runnable {
+ /**
+ *
+ */
+ @Override
+ public void run() {
+ networkGraphRefreshScheduled = false;
+ //TODO: it should refer to changed links only from DataChangeEvent above.
+ List<Link> links = getLinksFromTopology(DEFAULT_TOPOLOGY_ID);
+ networkGraphService.clear();// can remove this once changed links are addressed
+ if(links != null && !links.isEmpty()) {
+ networkGraphService.addLinks(links);
+ }
+ }
+
+ /**
+ * @param topologyId
+ * @return
+ */
+ private List<Link> getLinksFromTopology(String topologyId) {
+ InstanceIdentifier<Topology> topologyInstanceIdentifier = InstanceIdentifierUtils.generateTopologyInstanceIdentifier(topologyId);
+ Topology topology = (Topology) dataBrokerService.readOperationalData(topologyInstanceIdentifier);
+ return topology.getLink();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.util;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/* InstanceIdentifierUtils provides utility functions related to InstanceIdentifiers.
+ */
+public class InstanceIdentifierUtils {
+
+ /**
+ * Creates an Instance Identifier (path) for node with specified id
+ *
+ * @param nodeId
+ * @return
+ */
+ public static final InstanceIdentifier<Node> createNodePath(NodeId nodeId) {
+ return InstanceIdentifier.builder(Nodes.class) //
+ .child(Node.class, new NodeKey(nodeId)) //
+ .build();
+ }
+
+ /**
+ * Shorten's node child path to node path.
+ *
+ * @param nodeChild child of node, from which we want node path.
+ * @return
+ */
+ public static final InstanceIdentifier<Node> getNodePath(InstanceIdentifier<?> nodeChild) {
+ return nodeChild.firstIdentifierOf(Node.class);
+ }
+
+
+ /**
+ * Creates a table path by appending table specific location to node path
+ *
+ * @param nodePath
+ * @param tableKey
+ * @return
+ */
+ public static final InstanceIdentifier<Table> createTablePath(InstanceIdentifier<Node> nodePath, TableKey tableKey) {
+ return InstanceIdentifier.builder(nodePath)
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, tableKey)
+ .build();
+ }
+
+ /**
+ * Creates a path for particular flow, by appending flow-specific information
+ * to table path.
+ *
+ * @param table
+ * @param flowKey
+ * @return
+ */
+ public static InstanceIdentifier<Flow> createFlowPath(InstanceIdentifier<Table> table, FlowKey flowKey) {
+ return InstanceIdentifier.builder(table)
+ .child(Flow.class, flowKey)
+ .build();
+ }
+
+ /**
+ * Extract table id from table path.
+ *
+ * @param tablePath
+ * @return
+ */
+ public static Short getTableId(InstanceIdentifier<Table> tablePath) {
+ return tablePath.firstKeyOf(Table.class, TableKey.class).getId();
+ }
+
+ /**
+ * Extracts NodeConnectorKey from node connector path.
+ */
+ public static NodeConnectorKey getNodeConnectorKey(InstanceIdentifier<?> nodeConnectorPath) {
+ return nodeConnectorPath.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+ }
+
+ /**
+ * Extracts NodeKey from node path.
+ */
+ public static NodeKey getNodeKey(InstanceIdentifier<?> nodePath) {
+ return nodePath.firstKeyOf(Node.class, NodeKey.class);
+ }
+
+
+ //
+ public static final InstanceIdentifier<NodeConnector> createNodeConnectorIdentifier(String nodeIdValue,
+ String nodeConnectorIdValue) {
+ return InstanceIdentifier.builder(createNodePath(new NodeId(nodeIdValue))) //
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(nodeConnectorIdValue))) //
+ .build();
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @return
+ */
+ public static InstanceIdentifier<Node> generateNodeInstanceIdentifier(NodeConnectorRef nodeConnectorRef) {
+ return nodeConnectorRef.getValue().firstIdentifierOf(Node.class);
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @param flowTableKey
+ * @return
+ */
+ public static InstanceIdentifier<Table> generateFlowTableInstanceIdentifier(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) {
+ return InstanceIdentifier.builder(generateNodeInstanceIdentifier(nodeConnectorRef))
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, flowTableKey)
+ .build();
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @param flowTableKey
+ * @param flowKey
+ * @return
+ */
+ public static InstanceIdentifier<Flow> generateFlowInstanceIdentifier(NodeConnectorRef nodeConnectorRef,
+ TableKey flowTableKey,
+ FlowKey flowKey) {
+ return InstanceIdentifier.builder(generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey))
+ .child(Flow.class, flowKey)
+ .build();
+ }
+
+ public static InstanceIdentifier<Topology> generateTopologyInstanceIdentifier(String topologyId) {
+ return InstanceIdentifier.builder(NetworkTopology.class)
+ .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
+ .build();
+ }
+}
+
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.flow;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class FlowWriterServiceImplTest {
+ private DataBrokerService dataBrokerService;
+ private NodeConnectorRef srcNodeConnectorRef;
+ private NodeConnectorRef destNodeConnectorRef;
+ private MacAddress destMacAddress;
+ private MacAddress srcMacAddress;
+ private DataModificationTransaction dataModificationTransaction;
+ private NetworkGraphService networkGraphService;
+
+ @Before
+ public void init() {
+ dataBrokerService = mock(DataBrokerService.class);
+ networkGraphService = mock(NetworkGraphService.class);
+ //build source node connector ref
+ InstanceIdentifier<Nodes> srcNodesInstanceIdentifier
+ = InstanceIdentifier.builder(Nodes.class)
+ .build();
+ InstanceIdentifier<Node> srcNodeInstanceIdentifier
+ = InstanceIdentifier.builder(srcNodesInstanceIdentifier)
+ .child(Node.class, new NodeKey(new NodeId("openflow:1")))
+ .build();
+ InstanceIdentifier<NodeConnector> srcNodeConnectorInstanceIdentifier
+ = InstanceIdentifier.builder(srcNodeInstanceIdentifier)
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("openflow:1:2")))
+ .build();
+ srcNodeConnectorRef = new NodeConnectorRef(srcNodeConnectorInstanceIdentifier);
+
+ //build dest node connector ref
+ InstanceIdentifier<Nodes> nodesInstanceIdentifier
+ = InstanceIdentifier.builder(Nodes.class)
+ .build();
+ InstanceIdentifier<Node> nodeInstanceIdentifier
+ = InstanceIdentifier.builder(nodesInstanceIdentifier)
+ .child(Node.class, new NodeKey(new NodeId("openflow:2")))
+ .build();
+ InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+ = InstanceIdentifier.builder(nodeInstanceIdentifier)
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("openflow:2:2")))
+ .build();
+ destNodeConnectorRef = new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+ destMacAddress = new MacAddress("00:0a:95:9d:68:16");
+ srcMacAddress = new MacAddress("00:0a:95:8c:97:24");
+ dataModificationTransaction = mock(DataModificationTransaction.class);
+ when(dataBrokerService.beginTransaction()).thenReturn(dataModificationTransaction);
+ }
+
+ @Test
+ public void testFlowWriterServiceImpl_NPEWhenDataBrokerServiceIsNull() throws Exception {
+ try {
+ new FlowWriterServiceImpl(null, networkGraphService);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("dataBrokerService should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_NPEWhenNullSourceMacDestMacAndNodeConnectorRef() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ try {
+ flowWriterService.addMacToMacFlow(null, null, null);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("Destination mac address should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_NPEWhenSourceMacNullMac() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ try {
+ flowWriterService.addMacToMacFlow(null, null, destNodeConnectorRef);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("Destination mac address should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_NPEWhenNullSourceMacNodeConnectorRef() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ try {
+ flowWriterService.addMacToMacFlow(null, destMacAddress, null);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("Destination port should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_WhenNullSourceMac() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ flowWriterService.addMacToMacFlow(null, destMacAddress, destNodeConnectorRef);
+ verify(dataBrokerService, times(1)).beginTransaction();
+ verify(dataModificationTransaction, times(1)).commit();
+ }
+
+ @Test
+ public void testAddMacToMacFlow_WhenSrcAndDestMacAreSame() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ flowWriterService.addMacToMacFlow(new MacAddress(destMacAddress.getValue()), destMacAddress, destNodeConnectorRef);
+ verify(dataBrokerService, never()).beginTransaction();
+ verify(dataModificationTransaction, never()).commit();
+
+ }
+
+ @Test
+ public void testAddMacToMacFlow_SunnyDay() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ flowWriterService.addMacToMacFlow(srcMacAddress, destMacAddress, destNodeConnectorRef);
+ verify(dataBrokerService, times(1)).beginTransaction();
+ verify(dataModificationTransaction, times(1)).commit();
+ }
+
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Source;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class NetworkGraphDijkstraTest {
+ Link link1, link2, link3, link4, link5, link6, link7, link8, link9, link10,link11,link12;
+ Destination dest1, dest2, dest3, dest4, dest5, dest6,dest7,dest8,dest9,dest10,dest11,dest12;
+ Source src1, src2, src3, src4, src5, src6,src7,src8,src9,src10,src11,src12;
+ NodeId nodeId1 = new NodeId("openflow:1");
+ NodeId nodeId2 = new NodeId("openflow:2");
+ NodeId nodeId3 = new NodeId("openflow:3");
+ NodeId nodeId4 = new NodeId("openflow:4");
+ NodeId nodeId5 = new NodeId("openflow:5");
+ NodeId nodeId6 = new NodeId("openflow:6");
+ NodeId nodeId7 = new NodeId("openflow:7");
+ List<Link> links = new ArrayList<>();
+
+ @Before
+ public void init() {
+ link1 = mock(Link.class);
+ link2 = mock(Link.class);
+ link3 = mock(Link.class);
+ link4 = mock(Link.class);
+ link5 = mock(Link.class);
+ link6 = mock(Link.class);
+ link7 = mock(Link.class);
+ link8 = mock(Link.class);
+ link9 = mock(Link.class);
+ link10 = mock(Link.class);
+ link11 = mock(Link.class);
+ link12 = mock(Link.class);
+ dest1 = mock(Destination.class);
+ dest2 = mock(Destination.class);
+ dest3 = mock(Destination.class);
+ dest4 = mock(Destination.class);
+ dest5 = mock(Destination.class);
+ dest6 = mock(Destination.class);
+ dest7 = mock(Destination.class);
+ dest8 = mock(Destination.class);
+ dest9 = mock(Destination.class);
+ dest10 = mock(Destination.class);
+ dest11 = mock(Destination.class);
+ dest12 = mock(Destination.class);
+ src1 = mock(Source.class);
+ src2 = mock(Source.class);
+ src3 = mock(Source.class);
+ src4 = mock(Source.class);
+ src5 = mock(Source.class);
+ src6 = mock(Source.class);
+ src7 = mock(Source.class);
+ src8 = mock(Source.class);
+ src9 = mock(Source.class);
+ src10 = mock(Source.class);
+ src11 = mock(Source.class);
+ src12 = mock(Source.class);
+ when(link1.getSource()).thenReturn(src1);
+ when(link2.getSource()).thenReturn(src2);
+ when(link3.getSource()).thenReturn(src3);
+ when(link4.getSource()).thenReturn(src4);
+ when(link5.getSource()).thenReturn(src5);
+ when(link6.getSource()).thenReturn(src6);
+ when(link7.getSource()).thenReturn(src7);
+ when(link8.getSource()).thenReturn(src8);
+ when(link9.getSource()).thenReturn(src9);
+ when(link10.getSource()).thenReturn(src10);
+ when(link11.getSource()).thenReturn(src11);
+ when(link12.getSource()).thenReturn(src12);
+ when(link1.getDestination()).thenReturn(dest1);
+ when(link2.getDestination()).thenReturn(dest2);
+ when(link3.getDestination()).thenReturn(dest3);
+ when(link4.getDestination()).thenReturn(dest4);
+ when(link5.getDestination()).thenReturn(dest5);
+ when(link6.getDestination()).thenReturn(dest6);
+ when(link7.getDestination()).thenReturn(dest7);
+ when(link8.getDestination()).thenReturn(dest8);
+ when(link9.getDestination()).thenReturn(dest9);
+ when(link10.getDestination()).thenReturn(dest10);
+ when(link11.getDestination()).thenReturn(dest11);
+ when(link12.getDestination()).thenReturn(dest12);
+ when(src1.getSourceNode()).thenReturn(nodeId1);
+ when(dest1.getDestNode()).thenReturn(nodeId2);
+ when(src2.getSourceNode()).thenReturn(nodeId2);
+ when(dest2.getDestNode()).thenReturn(nodeId1);
+ when(src3.getSourceNode()).thenReturn(nodeId1);
+ when(dest3.getDestNode()).thenReturn(nodeId3);
+ when(src4.getSourceNode()).thenReturn(nodeId3);
+ when(dest4.getDestNode()).thenReturn(nodeId1);
+ when(src5.getSourceNode()).thenReturn(nodeId2);
+ when(dest5.getDestNode()).thenReturn(nodeId4);
+ when(src6.getSourceNode()).thenReturn(nodeId4);
+ when(dest6.getDestNode()).thenReturn(nodeId2);
+ when(src7.getSourceNode()).thenReturn(nodeId2);
+ when(dest7.getDestNode()).thenReturn(nodeId5);
+ when(src8.getSourceNode()).thenReturn(nodeId5);
+ when(dest8.getDestNode()).thenReturn(nodeId2);
+ when(src9.getSourceNode()).thenReturn(nodeId6);
+ when(dest9.getDestNode()).thenReturn(nodeId3);
+ when(src10.getSourceNode()).thenReturn(nodeId3);
+ when(dest10.getDestNode()).thenReturn(nodeId6);
+ when(src11.getSourceNode()).thenReturn(nodeId7);
+ when(dest11.getDestNode()).thenReturn(nodeId3);
+ when(src12.getSourceNode()).thenReturn(nodeId3);
+ when(dest12.getDestNode()).thenReturn(nodeId7);
+ links.add(link1);
+ links.add(link2);
+ links.add(link3);
+ links.add(link4);
+ links.add(link5);
+ links.add(link6);
+ links.add(link7);
+ links.add(link8);
+ links.add(link9);
+ links.add(link10);
+ links.add(link11);
+ links.add(link12);
+
+ }
+
+ @Test
+ public void testAddLinksAndGetPath() throws Exception {
+ NetworkGraphService networkGraphService = new NetworkGraphDijkstra();
+ networkGraphService.addLinks(links);
+ List<Link> path = networkGraphService.getPath(nodeId2, nodeId3);
+ assertEquals("path size is not as expected.", 2, path.size());
+ assertEquals("link source is not as expected.", nodeId2, path.get(0).getSource().getSourceNode());
+ assertEquals("link destination is not as expected.", nodeId1, path.get(0).getDestination().getDestNode());
+ path = networkGraphService.getPath(nodeId3, nodeId2);
+ assertEquals("path size is not as expected.", 2, path.size());
+ assertEquals("link source is not as expected.", nodeId3, path.get(0).getSource().getSourceNode());
+ assertEquals("link destination is not as expected.", nodeId1, path.get(0).getDestination().getDestNode());
+
+ path = networkGraphService.getPath(nodeId4, nodeId6);
+ assertEquals("path size is not as expected.", 4, path.size());
+ assertEquals("link source is not as expected.", nodeId4, path.get(0).getSource().getSourceNode());
+ assertEquals("link destination is not as expected.", nodeId2, path.get(0).getDestination().getDestNode());
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class TopologyLinkDataChangeHandlerTest {
+ NetworkGraphService networkGraphService;
+ DataBrokerService dataBrokerService;
+ DataChangeEvent dataChangeEvent;
+ Topology topology;
+ Link link;
+
+ @Before
+ public void init() {
+ networkGraphService = mock(NetworkGraphService.class);
+ dataBrokerService = mock(DataBrokerService.class);
+ dataChangeEvent = mock(DataChangeEvent.class);
+ link = mock(Link.class);
+ topology = mock(Topology.class);
+ }
+
+ @Test
+ public void testOnDataChange() throws Exception {
+ TopologyLinkDataChangeHandler topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataBrokerService, networkGraphService, 2);
+ Map<InstanceIdentifier<?>, DataObject> original = new HashMap<InstanceIdentifier<?>, DataObject>();
+ InstanceIdentifier<?> instanceIdentifier = InstanceIdentifierUtils.generateTopologyInstanceIdentifier("flow:1");
+ DataObject dataObject = mock(DataObject.class);
+ Map<InstanceIdentifier<?>, DataObject> updated = new HashMap<InstanceIdentifier<?>, DataObject>();
+ updated.put(instanceIdentifier, dataObject);
+ when(dataChangeEvent.getUpdatedOperationalData()).thenReturn(updated);
+ when(dataChangeEvent.getOriginalOperationalData()).thenReturn(original);
+ List<Link> links = new ArrayList<>();
+ links.add(link);
+ when(dataBrokerService.readOperationalData(instanceIdentifier)).thenReturn(topology);
+ when(topology.getLink()).thenReturn(links);
+
+ topologyLinkDataChangeHandler.onDataChanged(dataChangeEvent);
+ Thread.sleep(2100);
+ verify(networkGraphService, times(1)).addLinks(links);
+ }
+}
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.1-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+ <artifactId>l2switch-model</artifactId>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Import-Package>org.opendaylight.yangtools.yang.binding.annotations, *</Import-Package>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </instructions>
+ </configuration>
+ </plugin>
+ <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/sal
+ </outputBaseDir>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>target/site/models</outputBaseDir>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.wadl.generator.maven.WadlGenerator</codeGeneratorClass>
+ <outputBaseDir>target/site/models</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.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yangtools.version}</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+module l2-address-tracker {
+ yang-version 1;
+ namespace "urn:opendaylight:l2-address-tracker";
+ prefix l2-address-tracker;
+
+ import ietf-yang-types {
+ prefix yang;
+ revision-date 2010-09-24;
+ }
+ import opendaylight-inventory {
+ prefix inv;
+ revision-date 2013-08-19;
+ }
+
+ organization "Cisco Systems Inc";
+ contact
+ "Alex Fan <alefan@cisco.com>";
+ description
+ "YANG version of the L2 Address Tracker Data Model";
+
+ revision 2014-04-02 {
+ description
+ "L2 Address Tracker module draft.";
+ }
+
+ grouping l2-address {
+ leaf mac {
+ type yang:mac-address;
+ mandatory true;
+ description
+ "the mac address of the host.";
+ }
+ leaf node-connector-ref {
+ type inv:node-connector-ref;
+ }
+ }
+
+ container l2-addresses {
+ config false;
+ list l2-address {
+ key "mac";
+ uses l2-address;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+
+ <artifactId>l2switch.aggregator</artifactId>
+ <groupId>org.opendaylight.controller.samples.l2switch</groupId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>model</module>
+ <module>implementation</module>
+ </modules>
+
+</project>
<module>toaster</module>
<module>toaster-consumer</module>
<module>toaster-provider</module>
+ <module>l2switch</module>
</modules>
<profiles>
</plugins>
<pluginManagement>
<plugins>
- <!--This plugin's configuration is used to store Eclipse
+ <!--This plugin's configuration is used to store Eclipse
m2e settings only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>jersey-core</artifactId>
<version>${jersey.version}</version>
</dependency>
- <!-- <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId>
+ <!-- <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId>
<version>${jersey.version}</version> </dependency> -->
<dependency>
<groupId>com.sun.jersey</groupId>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
+ <artifactId>config-persister-file-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
<dependency>
InstanceIdentifier path = InstanceIdentifier.builder(InventoryUtils.INVENTORY_PATH)
.nodeWithKey(InventoryUtils.INVENTORY_NODE, InventoryUtils.INVENTORY_ID, "foo").toInstance();
-
+
InstanceIdentifier mountPointPath = path;
-
+
/** We retrive a mountpoint **/
MountProvisionInstance mountPoint = mountService.getMountPoint(mountPointPath);
CompositeNode data = mountPoint.readOperationalData(InstanceIdentifier.builder().node(CONFIG_MODULES)
mavenBundle(ODL, "yang-jmx-generator").versionAsInProject(),
mavenBundle(ODL, "logback-config").versionAsInProject(),
mavenBundle(ODL, "config-persister-api").versionAsInProject(),
- // mavenBundle(ODL,"config-persister-file-adapter").versionAsInProject(),
+ // mavenBundle(ODL,"config-persister-file-xml-adapter").versionAsInProject(),
mavenBundle(ODL, "protocol-framework").versionAsInProject(),
mavenBundle(ODL, "netconf-api").versionAsInProject(),
mavenBundle(ODL, "netconf-impl").versionAsInProject(),
netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
netconf.config.persister.1.properties.fileStorage=configuration/initial/
- netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
+ netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
netconf.config.persister.2.readonly=true
- netconf.config.persister.2.properties.fileStorage=configuration/current/controller.config.1.txt
+ netconf.config.persister.2.properties.fileStorage=configuration/current/controller.config.1.xml
- netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
- netconf.config.persister.3.properties.fileStorage=configuration/current/controller.config.2.txt
+ netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
+ netconf.config.persister.3.properties.fileStorage=configuration/current/controller.config.2.xml
netconf.config.persister.3.properties.numberOfBackups=3
</pre>
package org.opendaylight.controller.netconf.it;
-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 ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import io.netty.channel.ChannelFuture;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-import javax.management.ObjectName;
-import javax.xml.parsers.ParserConfigurationException;
-
import junit.framework.Assert;
-
+import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMXBean;
import org.opendaylight.controller.netconf.client.NetconfClient;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
-import ch.ethz.ssh2.Connection;
-import ch.ethz.ssh2.Session;
+import javax.management.ObjectName;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
-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;
public class NetconfITTest extends AbstractNetconfConfigTest {
// TODO refactor, pull common code up to AbstractNetconfITTest
- private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
+ private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830);
private static final String PASSWORD = "netconf";
private NetconfMessage getConfig, getConfigCandidate, editConfig,
- closeSession, startExi, stopExi;
+ closeSession, startExi, stopExi;
private DefaultCommitNotificationProducer commitNot;
private NetconfServerDispatcher dispatch;
ChannelFuture s = dispatch.createServer(tcpAddress);
s.await();
- clientDispatcher = new NetconfClientDispatcher( nettyThreadgroup, nettyThreadgroup, 5000);
+ clientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup, 5000);
}
private NetconfServerDispatcher createDispatcher(NetconfOperationServiceFactoryListenerImpl factoriesListener) {
yangDependencies.add(resourceAsStream);
}
}
- assertEquals("Some yang files were not found",emptyList(), failedToFind);
+ assertEquals("Some yang files were not found", emptyList(), failedToFind);
return yangDependencies;
}
protected List<ModuleFactory> getModuleFactories() {
return getModuleFactoriesS();
}
+
static List<ModuleFactory> getModuleFactoriesS() {
return Lists.newArrayList(new TestImplModuleFactory(), new DepTestImplModuleFactory(),
new NetconfTestImplModuleFactory());
@Test
public void testTwoSessions() throws Exception {
- try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 10000, clientDispatcher)) {
+ try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 10000, clientDispatcher)) {
try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 10000, clientDispatcher)) {
}
}
NetconfTestImplModuleMXBean proxy = configRegistryClient
.newMXBeanProxy(impl, NetconfTestImplModuleMXBean.class);
proxy.setTestingDep(dep);
- proxy.setSimpleShort((short)0);
+ proxy.setSimpleShort((short) 0);
transaction.commit();
return netconfClient;
}
- private void startSSHServer() throws Exception{
+ private void startSSHServer() throws Exception {
logger.info("Creating SSH server");
- StubUserManager um = new StubUserManager(USERNAME,PASSWORD);
- InputStream is = getClass().getResourceAsStream("/RSA.pk");
- AuthProvider ap = new AuthProvider(um, is);
- Thread sshServerThread = new Thread(NetconfSSHServer.start(10830,tcpAddress,ap));
+ StubUserManager um = new StubUserManager(USERNAME, PASSWORD);
+ String pem;
+ try (InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
+ pem = IOUtils.toString(is);
+ }
+ AuthProvider ap = new AuthProvider(um, pem);
+ Thread sshServerThread = new Thread(NetconfSSHServer.start(10830, tcpAddress, ap));
sshServerThread.setDaemon(true);
sshServerThread.start();
logger.info("SSH server on");
public void sshTest() throws Exception {
startSSHServer();
logger.info("creating connection");
- Connection conn = new Connection(sshAddress.getHostName(),sshAddress.getPort());
+ Connection conn = new Connection(sshAddress.getHostName(), sshAddress.getPort());
Assert.assertNotNull(conn);
logger.info("connection created");
conn.connect();
- boolean isAuthenticated = conn.authenticateWithPassword(USERNAME,PASSWORD);
+ boolean isAuthenticated = conn.authenticateWithPassword(USERNAME, PASSWORD);
assertTrue(isAuthenticated);
logger.info("user authenticated");
final Session sess = conn.openSession();
logger.info("user authenticated");
sess.getStdin().write(XmlUtil.toString(this.getConfig.getDocument()).getBytes());
- new Thread(){
+ new Thread() {
@Override
- public void run(){
- while (true){
+ public void run() {
+ while (true) {
byte[] bytes = new byte[1024];
int c = 0;
try {
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
- logger.info("got data:"+bytes);
- if (c == 0) break;
+ logger.info("got data:" + bytes);
+ if (c == 0) {
+ break;
+ }
}
}
}.join();
-<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">
+<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>
<groupId>org.opendaylight.controller</groupId>
<artifactId>usermanager</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
- <Bundle-Activator>org.opendaylight.controller.netconf.osgi.NetconfSSHActivator</Bundle-Activator>
+ <Bundle-Activator>org.opendaylight.controller.netconf.ssh.osgi.NetconfSSHActivator
+ </Bundle-Activator>
<Import-Package>
com.google.common.base,
ch.ethz.ssh2,
org.osgi.framework,
org.osgi.util.tracker,
org.slf4j,
+ org.bouncycastle.openssl
</Import-Package>
</instructions>
</configuration>
*/
package org.opendaylight.controller.netconf.ssh.authentication;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.commons.io.IOUtils;
import org.opendaylight.controller.sal.authorization.AuthResultEnum;
import org.opendaylight.controller.sal.authorization.UserLevel;
import org.opendaylight.controller.usermanager.IUserManager;
import org.opendaylight.controller.usermanager.UserConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
public class AuthProvider implements AuthProviderInterface {
- private static IUserManager um;
+ private static IUserManager um; //FIXME static mutable state, no locks
private static final String DEFAULT_USER = "netconf";
private static final String DEFAULT_PASSWORD = "netconf";
- private String PEM;
-
- private static final Logger logger = LoggerFactory.getLogger(AuthProvider.class);
-
- public AuthProvider(IUserManager ium,InputStream privateKeyFileInputStream) throws Exception {
+ private final String pem;
+ public AuthProvider(IUserManager ium, String pemCertificate) throws Exception {
+ checkNotNull(pemCertificate, "Parameter 'pemCertificate' is null");
AuthProvider.um = ium;
- if (AuthProvider.um == null){
+ if (AuthProvider.um == null) {
throw new Exception("No usermanager service available.");
}
List<String> roles = new ArrayList<String>(1);
roles.add(UserLevel.SYSTEMADMIN.toString());
- AuthProvider.um.addLocalUser(new UserConfig(DEFAULT_USER, DEFAULT_PASSWORD, roles));
-
- try {
- PEM = IOUtils.toString(privateKeyFileInputStream);
- } catch (IOException e) {
- logger.error("Error reading RSA key from file.");
- throw new IllegalStateException("Error reading RSA key from file.");
- }
+ AuthProvider.um.addLocalUser(new UserConfig(DEFAULT_USER, DEFAULT_PASSWORD, roles)); //FIXME hardcoded auth
+ pem = pemCertificate;
}
+
@Override
- public boolean authenticated(String username, String password) throws Exception {
- if (AuthProvider.um == null){
+ public boolean authenticated(String username, String password) throws Exception {
+ if (AuthProvider.um == null) {
throw new Exception("No usermanager service available.");
}
- AuthResultEnum authResult = AuthProvider.um.authenticate(username,password);
- if (authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC)){
- return true;
- }
- return false;
+ AuthResultEnum authResult = AuthProvider.um.authenticate(username, password);
+ return authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC);
}
@Override
- public char[] getPEMAsCharArray() throws Exception {
- if (null == PEM){
- logger.error("Missing RSA key string.");
- throw new Exception("Missing RSA key.");
- }
- return PEM.toCharArray();
+ public char[] getPEMAsCharArray() {
+ return pem.toCharArray();
}
@Override
public void addUserManagerService(IUserManager userManagerService) {
AuthProvider.um = userManagerService;
}
-
-
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.ssh.authentication;
+
+import org.apache.commons.io.FileUtils;
+import org.bouncycastle.openssl.PEMWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+
+public class PEMGenerator {
+ private static final Logger logger = LoggerFactory.getLogger(PEMGenerator.class);
+ private static final int KEY_SIZE = 4096;
+
+ public static String generateTo(File privateFile) throws Exception {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ SecureRandom sr = new SecureRandom();
+ keyGen.initialize(KEY_SIZE, sr);
+ KeyPair keypair = keyGen.generateKeyPair();
+ logger.info("Generating private key to {}", privateFile.getAbsolutePath());
+ String privatePEM = toString(keypair.getPrivate());
+ FileUtils.write(privateFile, privatePEM);
+ return privatePEM;
+ }
+
+ private static String toString(Key key) throws IOException {
+ try (StringWriter writer = new StringWriter()) {
+ try (PEMWriter pemWriter = new PEMWriter(writer)) {
+ pemWriter.writeObject(key);
+ }
+ return writer.toString();
+ }
+ }
+}
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.osgi;
+package org.opendaylight.controller.netconf.ssh.osgi;
import com.google.common.base.Optional;
-import java.io.FileInputStream;
-import java.net.InetSocketAddress;
+import org.apache.commons.io.IOUtils;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.opendaylight.controller.usermanager.IUserManager;
import org.osgi.framework.BundleActivator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
/**
* Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator
* starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket
@Override
public void removedService(ServiceReference<IUserManager> reference, IUserManager service) {
logger.trace("Removing service {} from netconf SSH. " +
- "SSH won't authenticate users until IUserManeger service will be started.", reference);
+ "SSH won't authenticate users until IUserManager service will be started.", reference);
removeUserManagerService();
}
};
if (sshSocketAddressOptional.isPresent()){
String path = NetconfConfigUtil.getPrivateKeyPath(context);
- path = path.replace("\\", "/");
+ path = path.replace("\\", "/"); // FIXME: shouldn't this convert lines to system dependent path separator?
if (path.equals("")){
throw new Exception("Missing netconf.ssh.pk.path key in configuration file.");
}
- try (FileInputStream fis = new FileInputStream(path)){
- AuthProvider authProvider = new AuthProvider(iUserManager,fis);
- this.server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress,authProvider);
+ File privateKeyFile = new File(path);
+ String privateKeyPEMString;
+ if (privateKeyFile.exists() == false) {
+ // generate & save to file
+ privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile);
+ } else {
+ // read from file
+ try (FileInputStream fis = new FileInputStream(path)) {
+ privateKeyPEMString = IOUtils.toString(fis);
+ } catch (IOException e) {
+ logger.error("Error reading RSA key from file '{}'", path);
+ throw new IllegalStateException("Error reading RSA key from file " + path);
+ }
}
+ AuthProvider authProvider = new AuthProvider(iUserManager, privateKeyPEMString);
+ this.server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress,authProvider);
Thread serverThread = new Thread(server,"netconf SSH server thread");
serverThread.setDaemon(true);
package org.opendaylight.controller.netconf.ssh.threads;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import ch.ethz.ssh2.AuthenticationResult;
import ch.ethz.ssh2.PtySettings;
import ch.ethz.ssh2.ServerAuthenticationCallback;
import ch.ethz.ssh2.ServerSession;
import ch.ethz.ssh2.ServerSessionCallback;
import ch.ethz.ssh2.SimpleServerSessionCallback;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
@ThreadSafe
public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback {
- private static final Logger logger = LoggerFactory.getLogger(SocketThread.class);
+ private static final Logger logger = LoggerFactory.getLogger(SocketThread.class);
private final Socket socket;
private final InetSocketAddress clientAddress;
public static void start(Socket socket,
InetSocketAddress clientAddress,
long sessionId,
- AuthProvider authProvider) throws IOException{
- Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket,clientAddress,sessionId,authProvider));
+ AuthProvider authProvider) throws IOException {
+ Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket, clientAddress, sessionId, authProvider));
netconf_ssh_socket_thread.setDaemon(true);
netconf_ssh_socket_thread.start();
}
+
private SocketThread(Socket socket,
InetSocketAddress clientAddress,
long sessionId,
this.socket = socket;
this.clientAddress = clientAddress;
this.sessionId = sessionId;
- this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/","");
+ this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/", "");
this.authProvider = authProvider;
}
public void run() {
conn = new ServerConnection(socket);
try {
- conn.setPEMHostKey(authProvider.getPEMAsCharArray(),"netconf");
+ conn.setPEMHostKey(authProvider.getPEMAsCharArray(), "netconf");
} catch (Exception e) {
logger.debug("Server authentication setup failed.");
}
try {
conn.connect();
} catch (IOException e) {
- logger.error("SocketThread error ",e);
+ logger.error("SocketThread error ", e);
}
}
+
@Override
- public ServerSessionCallback acceptSession(final ServerSession session)
- {
- SimpleServerSessionCallback cb = new SimpleServerSessionCallback()
- {
+ public ServerSessionCallback acceptSession(final ServerSession session) {
+ SimpleServerSessionCallback cb = new SimpleServerSessionCallback() {
@Override
- public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException
- {
- return new Runnable(){
+ public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException {
+ return new Runnable() {
@Override
- public void run()
- {
- if (subsystem.equals("netconf")){
+ public void run() {
+ if (subsystem.equals("netconf")) {
IOThread netconf_ssh_input = null;
- IOThread netconf_ssh_output = null;
+ IOThread netconf_ssh_output = null;
try {
String hostName = clientAddress.getHostName();
int portNumber = clientAddress.getPort();
logger.trace("echo socket created");
logger.trace("starting netconf_ssh_input thread");
- netconf_ssh_input = new IOThread(echoSocket.getInputStream(),ss.getStdin(),"input_thread_"+sessionId,ss,conn);
+ netconf_ssh_input = new IOThread(echoSocket.getInputStream(), ss.getStdin(), "input_thread_" + sessionId, ss, conn);
netconf_ssh_input.setDaemon(false);
netconf_ssh_input.start();
logger.trace("starting netconf_ssh_output thread");
- final String customHeader = "["+currentUser+";"+remoteAddressWithPort+";ssh;;;;;;]\n";
- netconf_ssh_output = new IOThread(ss.getStdout(),echoSocket.getOutputStream(),"output_thread_"+sessionId,ss,conn,customHeader);
+ final String customHeader = "[" + currentUser + ";" + remoteAddressWithPort + ";ssh;;;;;;]\n";
+ netconf_ssh_output = new IOThread(ss.getStdout(), echoSocket.getOutputStream(), "output_thread_" + sessionId, ss, conn, customHeader);
netconf_ssh_output.setDaemon(false);
netconf_ssh_output.start();
logger.error("SSH bridge could not create echo socket: {}", t.getMessage(), t);
try {
- if (netconf_ssh_input!=null){
+ if (netconf_ssh_input != null) {
netconf_ssh_input.join();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
- logger.error("netconf_ssh_input join error ",e);
+ logger.error("netconf_ssh_input join error ", e);
}
try {
- if (netconf_ssh_output!=null){
+ if (netconf_ssh_output != null) {
netconf_ssh_output.join();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
- logger.error("netconf_ssh_output join error ",e);
+ logger.error("netconf_ssh_output join error ", e);
}
}
} else {
- try {
- ss.getStdin().write("wrong subsystem requested - closing connection".getBytes());
- ss.close();
- } catch (IOException e) {
- logger.debug("excpetion while sending bad subsystem response",e);
- }
+ String reason = "Only netconf subsystem is supported, requested:" + subsystem;
+ closeSession(ss, reason);
}
}
};
}
+
+ public void closeSession(ServerSession ss, String reason) {
+ logger.trace("Closing session - {}", reason);
+ try {
+ ss.getStdin().write(reason.getBytes());
+ } catch (IOException e) {
+ logger.debug("Exception while closing session", e);
+ }
+ ss.close();
+ }
+
@Override
- public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException
- {
- return new Runnable()
- {
+ public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException {
+ return new Runnable() {
@Override
- public void run()
- {
- //noop
+ public void run() {
+ closeSession(ss, "PTY request not supported");
}
};
}
@Override
- public Runnable requestShell(final ServerSession ss) throws IOException
- {
- return new Runnable()
- {
+ public Runnable requestShell(final ServerSession ss) throws IOException {
+ return new Runnable() {
@Override
- public void run()
- {
- //noop
+ public void run() {
+ closeSession(ss, "Shell not supported");
}
};
}
}
@Override
- public String initAuthentication(ServerConnection sc)
- {
- logger.trace("Established connection with host {}",remoteAddressWithPort);
- return "Established connection with host "+remoteAddressWithPort+"\r\n";
+ public String initAuthentication(ServerConnection sc) {
+ logger.trace("Established connection with host {}", remoteAddressWithPort);
+ return "Established connection with host " + remoteAddressWithPort + "\r\n";
}
@Override
- public String[] getRemainingAuthMethods(ServerConnection sc)
- {
- return new String[] { ServerAuthenticationCallback.METHOD_PASSWORD };
+ public String[] getRemainingAuthMethods(ServerConnection sc) {
+ return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD};
}
@Override
- public AuthenticationResult authenticateWithNone(ServerConnection sc, String username)
- {
+ public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) {
return AuthenticationResult.FAILURE;
}
@Override
- public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password)
- {
+ public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) {
try {
- if (authProvider.authenticated(username,password)){
+ if (authProvider.authenticated(username, password)) {
currentUser = username;
- logger.trace("user {}@{} authenticated",currentUser,remoteAddressWithPort);
+ logger.trace("user {}@{} authenticated", currentUser, remoteAddressWithPort);
return AuthenticationResult.SUCCESS;
}
- } catch (Exception e){
+ } catch (Exception e) {
logger.warn("Authentication failed due to :" + e.getLocalizedMessage());
}
return AuthenticationResult.FAILURE;
@Override
public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
- byte[] publickey, byte[] signature)
- {
+ byte[] publickey, byte[] signature) {
return AuthenticationResult.FAILURE;
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.opendaylight.controller.usermanager.IUserManager;
+import org.opendaylight.controller.usermanager.UserConfig;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+
+// This test is intended to be verified using ssh
+@Ignore
+public class KeyGeneratorTest {
+
+ @Mock
+ private IUserManager iUserManager;
+ File tempFile;
+
+ @Before
+ public void setUp() throws IOException {
+ MockitoAnnotations.initMocks(this);
+ doReturn(null).when(iUserManager).addLocalUser(any(UserConfig.class));
+ tempFile = File.createTempFile("odltest", ".tmp");
+ tempFile.deleteOnExit();
+ }
+
+ @After
+ public void tearDown() {
+ assertTrue(tempFile.delete());
+ }
+
+ @Test
+ public void test() throws Exception {
+ String pem = PEMGenerator.generateTo(tempFile);
+
+ AuthProvider authProvider = new AuthProvider(iUserManager, pem);
+ InetSocketAddress inetSocketAddress = new InetSocketAddress(Inet4Address.getLoopbackAddress().getHostAddress(), 8383);
+ NetconfSSHServer server = NetconfSSHServer.start(1830, inetSocketAddress, authProvider);
+
+ Thread serverThread = new Thread(server,"netconf SSH server thread");
+ serverThread.start();
+ serverThread.join();
+ }
+}
package org.opendaylight.controller.netconf;
import ch.ethz.ssh2.Connection;
-import java.io.InputStream;
-import java.net.InetSocketAddress;
import junit.framework.Assert;
+import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+
public class SSHServerTest {
public void startSSHServer() throws Exception{
logger.info("Creating SSH server");
StubUserManager um = new StubUserManager(USER,PASSWORD);
- InputStream is = getClass().getResourceAsStream("/RSA.pk");
- AuthProvider ap = new AuthProvider(um, is);
+ String pem;
+ try(InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
+ pem = IOUtils.toString(is);
+ }
+ AuthProvider ap = new AuthProvider(um, pem);
NetconfSSHServer server = NetconfSSHServer.start(PORT,tcpAddress,ap);
sshServerThread = new Thread(server);
sshServerThread.setDaemon(true);
package org.opendaylight.controller.netconf.util.mapping;
-import java.util.Map;
-
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
+import java.util.Map;
+
public abstract class AbstractNetconfOperation implements NetconfOperation {
private final String netconfSessionIdForReporting;
@Override
public String toString() {
- final StringBuffer sb = new StringBuffer("AbstractConfigNetconfOperation{");
- sb.append("name=").append(getOperationName());
+ final StringBuffer sb = new StringBuffer(getClass().getName());
+ try {
+ sb.append("{name=").append(getOperationName());
+ } catch(UnsupportedOperationException e) {
+ // no problem
+ }
sb.append(", namespace=").append(getOperationNamespace());
sb.append(", session=").append(netconfSessionIdForReporting);
sb.append('}');
<artifactId>config-persister-api</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-client</artifactId>
/*
- * Copyright IBM Corporation, 2013. All rights reserved.
+ * Copyright IBM Corporation and others, 2013. 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,
@XmlElement (name="tenant_id")
String tenantID;
+ @XmlElement (name="ipv6_address_mode", nillable=true)
+ String ipV6AddressMode;
+
+ @XmlElement (name="ipv6_ra_mode", nillable=true)
+ String ipV6RaMode;
+
/* stores the OpenStackPorts associated with an instance
* used to determine if that instance can be deleted.
*/
this.tenantID = tenantID;
}
+ public String getIpV6AddressMode() { return ipV6AddressMode; }
+
+ public void setIpV6AddressMode(String ipV6AddressMode) { this.ipV6AddressMode = ipV6AddressMode; }
+
+ public String getIpV6RaMode() { return ipV6RaMode; }
+
+ public void setIpV6RaMode(String ipV6RaMode) { this.ipV6RaMode = ipV6RaMode; }
+
/**
* This method copies selected fields from the object and returns them
* as a new object, suitable for marshaling.
if (s.equals("tenant_id")) {
ans.setTenantID(this.getTenantID());
}
+ if (s.equals("ipv6_address_mode")) {
+ ans.setIpV6AddressMode(this.getIpV6AddressMode());
+ }
+ if (s.equals("ipv6_ra_mode")) {
+ ans.setIpV6RaMode(this.getIpV6RaMode());
+ }
}
return ans;
}
+ ", ipVersion=" + ipVersion + ", cidr=" + cidr + ", gatewayIP=" + gatewayIP + ", dnsNameservers="
+ dnsNameservers + ", allocationPools=" + allocationPools + ", hostRoutes=" + hostRoutes
+ ", enableDHCP=" + enableDHCP + ", tenantID=" + tenantID + ", myPorts=" + myPorts
- + ", gatewayIPAssigned=" + gatewayIPAssigned + "]";
+ + ", gatewayIPAssigned=" + gatewayIPAssigned + ", ipv6AddressMode=" + ipV6AddressMode
+ + ", ipv6RaMode=" + ipV6RaMode + "]";
}
}
/*
- * Copyright IBM Corporation, 2013. All rights reserved.
+ * Copyright IBM Corporation and others, 2013. 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,
@QueryParam("gateway_ip") String queryGatewayIP,
@QueryParam("enable_dhcp") String queryEnableDHCP,
@QueryParam("tenant_id") String queryTenantID,
+ @QueryParam("ipv6_address_mode") String queryIpV6AddressMode,
+ @QueryParam("ipv6_ra_mode") String queryIpV6RaMode,
// pagination
@QueryParam("limit") String limit,
@QueryParam("marker") String marker,
(queryCIDR == null || queryCIDR.equals(oSS.getCidr())) &&
(queryGatewayIP == null || queryGatewayIP.equals(oSS.getGatewayIP())) &&
(queryEnableDHCP == null || queryEnableDHCP.equals(oSS.getEnableDHCP())) &&
- (queryTenantID == null || queryTenantID.equals(oSS.getTenantID()))) {
+ (queryTenantID == null || queryTenantID.equals(oSS.getTenantID())) &&
+ (queryIpV6AddressMode == null || queryIpV6AddressMode.equals(oSS.getIpV6AddressMode())) &&
+ (queryIpV6RaMode == null || queryIpV6RaMode.equals(oSS.getIpV6RaMode()))){
if (fields.size() > 0) {
ans.add(extractFields(oSS,fields));
} else {
if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
if (type == UpdateType.ADDED) {
edgeMap.put(dst, edge);
- } else {
+ } else if (type == UpdateType.REMOVED) {
edgeMap.remove(dst);
}
} else {
*/
if (type == UpdateType.ADDED) {
prodMap.put(dst, edge);
- } else {
+ } else if (type == UpdateType.REMOVED) {
prodMap.remove(dst);
}
}