+++ /dev/null
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.opendaylight</artifactId>
- <version>1.4.1-SNAPSHOT</version>
- <relativePath>../../commons/opendaylight</relativePath>
- </parent>
-
- <artifactId>controller-maven-plugin</artifactId>
- <version>0.1.0-SNAPSHOT</version>
- <packaging>maven-plugin</packaging>
-
- <name>controller-maven-plugin</name>
- <description>Maven Plugin for controlling the launch of the controller.</description>
- <url>http://maven.apache.org</url>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- </properties>
-
- <dependencies>
- <dependency>
- <!-- controller mojos depend on the api module -->
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-plugin-api</artifactId>
- <version>2.0</version>
- </dependency>
- <dependency>
- <!-- controller mojos use the annotations defined in this module -->
- <groupId>org.apache.maven.plugin-tools</groupId>
- <artifactId>maven-plugin-annotations</artifactId>
- <version>3.2</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <!-- needed for handling processes -->
- <groupId>com.sun</groupId>
- <artifactId>tools</artifactId>
- <scope>system</scope>
- <version>7</version>
- <systemPath>${java.home}/../lib/tools.jar</systemPath>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.8.1</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <!-- plugin builder -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-plugin-plugin</artifactId>
- <version>3.2</version>
- <configuration>
- <goalPrefix>controller-maven-plugin</goalPrefix>
- <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
- </configuration>
- <executions>
- <execution>
- <id>mojo-descriptor</id>
- <goals>
- <goal>descriptor</goal>
- </goals>
- </execution>
- <execution>
- <id>help-goal</id>
- <goals>
- <goal>helpmojo</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.maven.plugin;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugins.annotations.Parameter;
-
-import org.opendaylight.controller.maven.plugin.util.JavaProcess;
-import org.opendaylight.controller.maven.plugin.util.ProcessMonitor;
-
-/**
- * Base controller mojo which handles common operations
- */
-public abstract class AbstractControllerMojo extends AbstractMojo {
- public static final String OS_NAME = System.getProperty("os.name");
- public static final boolean WIN = OS_NAME.toUpperCase().contains("WINDOWS");
- public static final String JAVA_HOME = "JAVA_HOME";
- public static final boolean skip = Boolean.getBoolean("controller.startup.skip");
- public static final String MAIN_CLASS = "org.eclipse.equinox.launcher.Main";
- public static final String CTRL_PROP = "opendaylight.controller";
-
- /**
- * The home directory where controller is installed
- */
- @Parameter( required = false )
- protected File controllerHome;
-
- /**
- * The address on which controller is listening
- */
- @Parameter( defaultValue = "localhost")
- protected String controllerHost;
-
- /**
- * The admin web port
- */
- @Parameter( defaultValue = "8080")
- protected int controllerWebPort;
-
- /**
- * The openflow port
- */
- @Parameter( defaultValue = "6633")
- protected int controllerOFPort;
-
- /**
- * Additional environment variables passed when starting the controller
- * process.
- */
- @Parameter(required = false)
- protected Properties controllerShellVariables;
-
- /**
- * The script name to invoke
- */
- @Parameter(required = false)
- protected String controllerStartScriptName;
-
- /**
- * The username
- */
- @Parameter(required = false)
- protected String controllerUsername;
-
- /**
- * The password
- */
- @Parameter(required = false)
- protected String controllerPassword;
-
- /**
- * pidFile location
- */
- @Parameter(required = false)
- protected File pidFile;
-
- protected final ProcessMonitor procMon = ProcessMonitor.load();
-
- public abstract void start() throws MojoExecutionException, MojoFailureException;
-
- public void execute() throws MojoExecutionException, MojoFailureException {
- if (skip) return;
- validateArgs();
- start();
- }
-
- protected URL getWebUrl() {
- try {
- return new URL("http", controllerHost, controllerWebPort, "/");
- } catch (MalformedURLException e) {
- throw new IllegalArgumentException(
- "controller host:port is Malformed: " + controllerHost + " " + controllerWebPort, e);
- }
-
- }
-
- protected void validateArgs() throws IllegalArgumentException {
- // System property and environment variable override the default setting
- String odlHome = System.getProperty("controllerHome");
- if (odlHome != null) {
- controllerHome = new File(odlHome);
- }
- if (controllerHome == null) {
- getLog().error("controllerHome cannot be determined from controllerHome "
- + "property or ONE_HOME env variable");
- throw new IllegalArgumentException("controllerHome cannot be determined.");
- }
- if (!controllerHome.exists()) {
- throw new IllegalArgumentException(
- "controllerHome does not exist: " + controllerHome);
- }
- if (controllerUsername == null) {
- controllerUsername = System.getProperty("controllerUsername");
- }
- if (controllerPassword == null) {
- controllerPassword= System.getProperty("controllerPassword");
- }
- URL u = getWebUrl();
- getLog().info("Controller Home : " + controllerHome);
- getLog().info("Controller Url : " + u);
- getLog().info("Controller credentials: " + controllerUsername
- + "/" + controllerPassword);
- }
-
- protected Process invokeScript(List<String> args, String log)
- throws MojoFailureException, MojoExecutionException
- {
- ProcessBuilder pb = new ProcessBuilder();
- List<String> cmd = new ArrayList<String>();
- cmd.add(getScript());
- if (args != null) {
- for (String s : args) {
- // on windows args containing equals symbols need to be quoted
- if (WIN && s.contains("=") && !s.startsWith("\"")) {
- cmd.add("\"" + s + "\"");
- } else {
- cmd.add(s);
- }
- }
- }
- pb.command(cmd);
- pb.directory(controllerHome);
- pb.redirectErrorStream(true);
- pb.inheritIO();
- Map<String,String> env = pb.environment();
- if (controllerShellVariables != null) {
- for (Enumeration e = controllerShellVariables.propertyNames(); e.hasMoreElements();) {
- String n = (String) e.nextElement();
- env.put(n, controllerShellVariables.getProperty(n));
- }
- }
- String jh = env.get(JAVA_HOME);
- if (jh == null) env.put(JAVA_HOME, System.getProperty("java.home"));
- try {
- getLog().info("Invoking process " + pb.command());
- return pb.start();
- } catch (IOException e) {
- throw new MojoExecutionException(e.getMessage());
- }
- }
-
- private String getScript() throws MojoFailureException {
- File script = null;
- if (controllerStartScriptName != null && !"".equals(controllerStartScriptName) ) {
- script = new File(controllerStartScriptName);
- if (!script.exists()) {
- // try relative path
- script = new File(controllerHome, controllerStartScriptName);
- }
- if (script.exists()) return script.getAbsolutePath();
- throw new MojoFailureException("Script not found: " + controllerStartScriptName);
- }
- // try default
- script = new File(controllerHome, "run." + (WIN ? "bat" : "sh") );
- if (script.exists()) return script.getAbsolutePath();
- throw new MojoFailureException("Cannot find a default script to launch.");
- }
-
- protected boolean canConnect() {
- try {
- URL url = getWebUrl();
- HttpURLConnection con;
- con = (HttpURLConnection) url.openConnection();
- return (con.getResponseCode() > 0);
- } catch (IOException e) {
- return false;
- }
- }
-
- public void killControllers() {
- getLog().info("Checking environment for stray processes.");
- List<JavaProcess> jvms = procMon.getProcesses(MAIN_CLASS, CTRL_PROP);
- for (JavaProcess j : jvms) {
- getLog().info("Killing running process: " + j);
- ProcessMonitor.kill(j.getPid());
- }
- // cleanup pid files
- getLog().info("Checking left over pid file: " + pidFile);
- if (pidFile != null && pidFile.exists()) {
- getLog().info("Cleaning up pid file : " + pidFile);
- pidFile.delete();
- }
- }
-
- public boolean isControllerRunning() {
- return !procMon.getProcesses(MAIN_CLASS, CTRL_PROP).isEmpty();
- }
-
-}
+++ /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.maven.plugin;
-
-import java.net.MalformedURLException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugins.annotations.LifecyclePhase;
-import org.apache.maven.plugins.annotations.Mojo;
-import org.apache.maven.plugins.annotations.Parameter;
-
-
-/**
- * Starts the controller
- */
-@Mojo( name = "run", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST )
-public class StartControllerMojo extends AbstractControllerMojo {
- public static final String REDIRECT_LOG = "controller.out";
-
- /**
- * The timeout value for starting the controller. Defaults to 60 secs
- */
- @Parameter(defaultValue = "60")
- public int timeoutSecs = 60;
-
- /**
- * The startArgs for starting the controller
- */
- @Parameter(required = false)
- protected List<String> startArgs = new ArrayList<String>();
-
- /**
- * The time to wait after successfully connecting to the controller and
- * before returning from execution.
- */
- @Parameter(required = false)
- protected int warmupTimeSecs = 10;
-
-
- @Override
- public void start() throws MojoExecutionException, MojoFailureException {
- killControllers();
- // if we can still connect to a controller, bail out
- if (canConnect()) {
- getLog().error("A controller is already running. Shutdown and retry.");
- throw new MojoFailureException("Controller is already running.");
- }
- startArgs.add("-D" + CTRL_PROP);
- Process process = invokeScript(startArgs, REDIRECT_LOG);
- getLog().info("Controller starting... (waiting for open ports)");
- try {
- waitForListening(process);
- getLog().info("Controller port open. Waiting for warmup: "
- + warmupTimeSecs);
- Thread.sleep(warmupTimeSecs*1000);
- } catch (Exception e) {
- throw new MojoExecutionException(e.getMessage());
- }
- getLog().info("Controller started successfully.");
- }
-
- protected boolean waitForListening(Process process)
- throws MalformedURLException, InterruptedException, MojoExecutionException
- {
- long timeElapsedMillis = 0L;
- long sleepTimeMillis = 2000L; // 2 secs
- long timeoutMillis = timeoutSecs * 1000;
-
- while (timeElapsedMillis < timeoutMillis) {
- long timeRemaining = timeoutMillis - timeElapsedMillis;
- sleepTimeMillis *= 2;
- long toSleep = (sleepTimeMillis > timeRemaining)
- ? timeRemaining : sleepTimeMillis;
- Thread.sleep(toSleep);
- timeElapsedMillis += toSleep;
- if (canConnect()) {
- return true;
- }
- if (!isControllerRunning()) {
- throw new MojoExecutionException("Process seems to have exited prematurely.");
- }
- }
- return false;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.maven.plugin;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugins.annotations.LifecyclePhase;
-import org.apache.maven.plugins.annotations.Mojo;
-
-/**
- * Stop controller
- */
-@Mojo( name = "stop", defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST )
-public class StopControllerMojo extends AbstractControllerMojo {
- private static final boolean SKIP_STOP = Boolean.getBoolean("skipControllerStop");
-
- @Override
- public void start() throws MojoExecutionException, MojoFailureException {
- if (SKIP_STOP) {
- getLog().info("Controller STOP is skipped per configuration " +
- "(-DskipControllerStop=true).");
- return;
- }
- if (canConnect()) {
- List<String> args = new ArrayList<String>();
- args.add("-stop");
- Process proc = invokeScript(args, null);
- try {
- int status = proc.waitFor();
- if (status == 0) {
- getLog().info("Controller stopped.");
- } else {
- getLog().error("Error stopping controller. See stdout log for details.");
- }
- } catch (InterruptedException ie) {
- throw new MojoExecutionException("Error stopping controller : " + ie.getMessage());
- }
- } else {
- getLog().info("Controller not running.");
- }
- // cleanup for any hung processes
- killControllers();
- }
-
-}
+++ /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.maven.plugin.util;
-
-import java.util.Properties;
-
-public class JavaProcess {
- private final int pid;
- private final String mainClass;
- private final Properties systemProperties = new Properties();
-
- public JavaProcess(int id, String cls) {
- this.pid = id;
- this.mainClass = cls;
- }
-
- public void setSystemProperties(String line) {
- if (line == null || line.length() == 0) return;
- String[] tokens = line.split("\\s");
- for (String t : tokens) setSystemProperty(t);
- }
-
- public void setSystemProperty(String line) {
- if (line.startsWith("-D")) {
- int x = line.indexOf('=');
- if (x > -1) {
- systemProperties.put(line.substring(2, x), line.substring(x+1));
- } else {
- systemProperties.put(line.substring(2), "");
- }
- }
- }
-
- public int getPid() {
- return pid;
- }
-
- public String getMainClass() {
- return mainClass;
- }
-
- public Properties getSystemProperties() {
- return systemProperties;
- }
-
- @Override
- public String toString() {
- return "pid:" + pid + " class:" + mainClass +
- " system-properties:" + systemProperties.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.maven.plugin.util;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Uses JPS tool to monitor java local processes
- */
-public class JpsProcessMonitor extends ProcessMonitor {
- private final String jpsTool;
-
- public JpsProcessMonitor() {
- String jh = System.getProperty("java.home");
- File jps = new File(jh + SEP + "bin" + SEP + "jps");
- if (!jps.exists()) {
- // try one dir above
- jps = new File(jh + SEP + ".." + SEP + "bin" + SEP + "jps");
- if (!jps.exists()) {
- throw new IllegalStateException("jps tool cannot be located.");
- }
- }
- jpsTool = jps.getAbsolutePath();
- }
-
- @Override
- public List<JavaProcess> getProcesses() {
- if (jpsTool == null) return super.getProcesses();
- List<JavaProcess> jvms = new ArrayList<JavaProcess>();
- try {
- ProcessBuilder pb = new ProcessBuilder();
- pb.command(new String[] { jpsTool, "-mlvV"} );
- pb.redirectErrorStream(true);
- Process process = pb.start();
- BufferedReader br = new BufferedReader(
- new InputStreamReader(process.getInputStream()));
- String line = null;
- while ( (line = br.readLine()) != null) {
- JavaProcess j = parseLine(line);
- if (j != null) jvms.add(j);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return jvms;
- }
-
- public static JavaProcess parseLine(String line) {
- String[] tokens = line.split("\\s");
- if (tokens.length < 2) {
- System.out.println("Unable to parse line: " + line);
- return null;
- }
- int idx = 0;
- int pid = Integer.parseInt(tokens[idx++]);
- String mainClass = "";
- if (!tokens[idx].startsWith("-")) {
- mainClass = tokens[idx++];
- }
- JavaProcess proc = new JavaProcess(pid, mainClass);
- for (int i=idx; i<tokens.length; i++) {
- if (tokens[i].startsWith("-D")) {
- proc.setSystemProperty(tokens[i]);
- }
- }
- return proc;
- }
-
-}
+++ /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.maven.plugin.util;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class ProcessMonitor {
- public static final String SEP = File.pathSeparator;
- public static final boolean WIN =
- System.getProperty("os.name").toLowerCase().indexOf("windows") > -1;
-
-
-
- public void log(String msg) {
- System.out.println("" + msg);
- }
-
- public List<JavaProcess> getProcesses() {
- return Collections.emptyList();
- }
-
- public List<JavaProcess> getProcesses(String mainClass, String systemPropertyKey) {
- List<JavaProcess> result = new ArrayList<JavaProcess>();
- for (JavaProcess info : getProcesses()) {
- if (info.getMainClass().equals(mainClass)) {
- if (info.getSystemProperties().containsKey(systemPropertyKey)) {
- result.add(info);
- }
- }
- }
- return result;
- }
-
- public int kill(String mainClass) {
- for (JavaProcess info : getProcesses()) {
- if (info.getMainClass().equals(mainClass)) {
- log("Killing process matching class: " + mainClass);
- return kill(info.getPid());
- }
- }
- return -1;
- }
-
- public static int kill(int pid) {
- String cmd = WIN ? "TASKKILL /F /PID " + pid : "kill -SIGTERM " + pid;
- try {
- Process p = Runtime.getRuntime().exec(cmd);
- p.waitFor();
- return p.exitValue();
- } catch (Exception e) {
- e.printStackTrace();
- return -1;
- }
- }
-
- public static ProcessMonitor load() {
- // load the providers dynamically to allow error handling
- ProcessMonitor pm = load("org.opendaylight.controller.maven.plugin.util.VMProcessMonitor");
- if (pm == null) {
- pm = load("org.opendaylight.controller.maven.plugin.util.JpsProcessMonitor");
- }
- return (pm == null ? new ProcessMonitor() : pm);
- }
-
- private static ProcessMonitor load(String clz) {
- try {
- Class c = Class.forName(clz);
- return (ProcessMonitor) c.newInstance();
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- // simple driver for basic manual testing
- public static void main(String[] args) throws Exception {
- ProcessMonitor pm = ProcessMonitor.load();
- System.out.println("==== " + pm);
- for (JavaProcess info : pm.getProcesses()) {
- System.out.println(info.toString());
- }
- pm.kill("Foo");
- System.out.println("==== controller processses ");
- for (JavaProcess info : pm.getProcesses(
- "org.eclipse.equinox.launcher.Main", "opendaylight.controller"))
- {
- System.out.println(info.toString());
- pm.kill(info.getPid());
- }
- }
-
-}
+++ /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.maven.plugin.util;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-import sun.jvmstat.monitor.HostIdentifier;
-import sun.jvmstat.monitor.MonitoredHost;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.MonitoredVmUtil;
-import sun.jvmstat.monitor.VmIdentifier;
-
-public class VMProcessMonitor extends ProcessMonitor {
-
- @Override
- public List<JavaProcess> getProcesses() {
- Set<Integer> activeVmPids = null;
- List<JavaProcess> result = new ArrayList<JavaProcess>();
- MonitoredHost monitoredHost = null;
- MonitoredVm mvm = null;
- try {
- monitoredHost = MonitoredHost.getMonitoredHost(
- new HostIdentifier((String) null));
- activeVmPids = monitoredHost.activeVms();
- } catch (Exception e) {
- throw new IllegalStateException("Error accessing VM", e);
- }
- for (Integer vmPid : activeVmPids) {
- try {
- mvm = monitoredHost.getMonitoredVm(
- new VmIdentifier(vmPid.toString()));
- JavaProcess proc = new JavaProcess(vmPid,
- MonitoredVmUtil.mainClass(mvm, true));
- proc.setSystemProperties(MonitoredVmUtil.jvmArgs(mvm));
- proc.setSystemProperties(MonitoredVmUtil.jvmFlags(mvm));
- result.add(proc);
- } catch(Exception e2) {
- log("Error connecting to pid: " + vmPid + " reason:"
- + e2.getMessage());
- e2.printStackTrace();
- } finally {
- if (mvm != null) {
- mvm.detach();
- }
- }
- }
- return result;
- }
-
-
-}
<artifactId>junit</artifactId>
</dependency>
<!-- Add Pax Exam -->
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-container-native</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>
<jacoco.version>0.5.3.201107060350</jacoco.version>
<enforcer.version>1.3.1</enforcer.version>
<bundle.plugin.version>2.3.7</bundle.plugin.version>
- <install.plugin.version>2.5</install.plugin.version>
- <enforcer.plugin.version>1.3.1</enforcer.plugin.version>
<junit.version>4.8.1</junit.version>
<bgpcep.version>0.3.0-SNAPSHOT</bgpcep.version>
<yangtools.version>0.5.9-SNAPSHOT</yangtools.version>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<sourceDirectory>${project.basedir}</sourceDirectory>
<includes>**\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat</includes>
- <excludes>**\/target\/,**\/bin\/</excludes>
+ <excludes>**\/target\/,**\/bin\/,**\/target-ide\/</excludes>
</configuration>
</plugin>
<plugin>
*/
boolean isHealthy();
+ /**
+ * @return module factory names available in the system
+ */
Set<String> getAvailableModuleNames();
+
+
/**
* Find all runtime beans
*
*/
void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException;
+ /**
+ * @return qnames of all ModuleFactory instances in the system
+ */
+ Set<String> getAvailableModuleFactoryQNames();
+
}
import org.opendaylight.controller.config.manager.impl.osgi.BeanToOsgiServiceManager;
import org.opendaylight.controller.config.manager.impl.osgi.BeanToOsgiServiceManager.OsgiRegistration;
import org.opendaylight.controller.config.manager.impl.util.LookupBeansUtil;
+import org.opendaylight.controller.config.manager.impl.util.ModuleQNameUtil;
import org.opendaylight.controller.config.spi.Module;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.osgi.framework.BundleContext;
private final ModuleFactoriesResolver resolver;
private final MBeanServer configMBeanServer;
- @GuardedBy("this")
- private final BundleContext bundleContext;
-
@GuardedBy("this")
private long version = 0;
@GuardedBy("this")
// internal jmx server shared by all transactions
private final MBeanServer transactionsMBeanServer;
+ // Used for finding new factory instances for default module functionality
@GuardedBy("this")
private List<ModuleFactory> lastListOfFactories = Collections.emptyList();
// constructor
public ConfigRegistryImpl(ModuleFactoriesResolver resolver,
- BundleContext bundleContext, MBeanServer configMBeanServer) {
- this(resolver, bundleContext, configMBeanServer,
+ MBeanServer configMBeanServer) {
+ this(resolver, configMBeanServer,
new BaseJMXRegistrator(configMBeanServer));
}
// constructor
public ConfigRegistryImpl(ModuleFactoriesResolver resolver,
- BundleContext bundleContext, MBeanServer configMBeanServer,
+ MBeanServer configMBeanServer,
BaseJMXRegistrator baseJMXRegistrator) {
this.resolver = resolver;
this.beanToOsgiServiceManager = new BeanToOsgiServiceManager();
- this.bundleContext = bundleContext;
this.configMBeanServer = configMBeanServer;
this.baseJMXRegistrator = baseJMXRegistrator;
this.registryMBeanServer = MBeanServerFactory
}
};
- ConfigTransactionLookupRegistry txLookupRegistry = new ConfigTransactionLookupRegistry(new TransactionIdentifier(
- transactionName), factory);
Map<String, Map.Entry<ModuleFactory, BundleContext>> allCurrentFactories = Collections.unmodifiableMap(
resolver.getAllFactories());
+ ConfigTransactionLookupRegistry txLookupRegistry = new ConfigTransactionLookupRegistry(new TransactionIdentifier(
+ transactionName), factory, allCurrentFactories);
ServiceReferenceWritableRegistry writableRegistry = ServiceReferenceRegistryImpl.createSRWritableRegistry(
readableSRRegistry, txLookupRegistry, allCurrentFactories);
public synchronized String getServiceInterfaceName(String namespace, String localName) {
return readableSRRegistry.getServiceInterfaceName(namespace, localName);
}
+
+ @Override
+ public Set<String> getAvailableModuleFactoryQNames() {
+ return ModuleQNameUtil.getQNames(resolver.getAllFactories());
+ }
+
}
/**
Collections.sort(result);
return result;
}
+
+
}
/**
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.lang.String.format;
return txLookupRegistry.getTransactionIdentifier();
}
+ @Override
+ public Set<String> getAvailableModuleFactoryQNames() {
+ return txLookupRegistry.getAvailableModuleFactoryQNames();
+ }
+
}
import org.opendaylight.controller.config.manager.impl.jmx.TransactionJMXRegistrator;
import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator;
import org.opendaylight.controller.config.manager.impl.util.LookupBeansUtil;
+import org.opendaylight.controller.config.manager.impl.util.ModuleQNameUtil;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.osgi.framework.BundleContext;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.ObjectName;
import java.io.Closeable;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
/**
private final TransactionJMXRegistrator transactionJMXRegistrator;
private final TransactionIdentifier transactionIdentifier;
private final TransactionModuleJMXRegistrator txModuleJMXRegistrator;
+ private final Map<String, Map.Entry<ModuleFactory, BundleContext>> allCurrentFactories;
ConfigTransactionLookupRegistry(TransactionIdentifier transactionIdentifier,
- TransactionJMXRegistratorFactory factory) {
+ TransactionJMXRegistratorFactory factory, Map<String, Entry<ModuleFactory, BundleContext>> allCurrentFactories) {
this.transactionIdentifier = transactionIdentifier;
this.transactionJMXRegistrator = factory.create();
this.txModuleJMXRegistrator = transactionJMXRegistrator.createTransactionModuleJMXRegistrator();
+ this.allCurrentFactories = allCurrentFactories;
}
private void checkTransactionName(ObjectName objectName) {
public void registerMBean(ConfigTransactionControllerInternal transactionController, ObjectName controllerObjectName) throws InstanceAlreadyExistsException {
transactionJMXRegistrator.registerMBean(transactionController, controllerObjectName);
}
+
+ @Override
+ public Set<String> getAvailableModuleFactoryQNames() {
+ return ModuleQNameUtil.getQNames(allCurrentFactories);
+ }
+
}
interface TransactionJMXRegistratorFactory {
public void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException {
throw new InstanceNotFoundException("Cannot find " + objectName);
}
+
+ @Override
+ public Set<String> getAvailableModuleFactoryQNames() {
+ throw new UnsupportedOperationException();
+ }
};
return new ServiceReferenceRegistryImpl(Collections.<String, ModuleFactory>emptyMap(), lookupRegistry,
Collections.<String /* qName */, Map<String /* refName */, ModuleIdentifier>>emptyMap());
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.List;
import java.util.Map;
return moduleFactories;
}
+ public Map<String, Entry<ModuleFactory, BundleContext>> getModuleNamesToConfigBeanFactories() {
+ return moduleNamesToConfigBeanFactories;
+ }
}
new BundleContextBackedModuleFactoriesResolver(context);
MBeanServer configMBeanServer = ManagementFactory.getPlatformMBeanServer();
configRegistry = new ConfigRegistryImpl(
- bundleContextBackedModuleFactoriesResolver, context,
- configMBeanServer);
+ bundleContextBackedModuleFactoriesResolver, configMBeanServer);
// register config registry to OSGi
configRegistryServiceRegistration = context.registerService(ConfigRegistryImpl.class, configRegistry, null);
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.manager.impl.util;
+
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.yangtools.yang.binding.annotations.ModuleQName;
+import org.osgi.framework.BundleContext;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class ModuleQNameUtil {
+
+ public static Set<String> getQNames(Map<String, Entry<ModuleFactory, BundleContext>> resolved) {
+ Set<String> result = new HashSet<>();
+ for (Entry<ModuleFactory, BundleContext> entry : resolved.values()) {
+ Class<?> inspected = entry.getKey().getClass();
+ if (inspected.isInterface()) {
+ throw new IllegalArgumentException("Unexpected interface " + inspected);
+ }
+ ModuleQName annotation = null;
+ while(annotation == null && inspected != null) {
+ annotation = inspected.getAnnotation(ModuleQName.class);
+ inspected = inspected.getSuperclass();
+ }
+ if (annotation != null) {
+ // FIXME
+ String qName = "(" + annotation.namespace() + "?revision=" + annotation.revision() + ")" + annotation.name();
+ result.add(qName);
+ }
+ }
+ return result;
+ }
+
+}
factory, factory);
configRegistry = new ConfigRegistryImpl(resolver,
- context, ManagementFactory.getPlatformMBeanServer());
+ ManagementFactory.getPlatformMBeanServer());
configRegistry.beginConfig();
fail();
internalJmxRegistrator = new InternalJMXRegistrator(platformMBeanServer);
baseJmxRegistrator = new BaseJMXRegistrator(internalJmxRegistrator);
- configRegistry = new ConfigRegistryImpl(resolver, mockedContext,
+ configRegistry = new ConfigRegistryImpl(resolver,
platformMBeanServer, baseJmxRegistrator);
try {
configRegistryJMXRegistrator.registerToJMX(configRegistry);
@Before
public void setUp() throws Exception {
- configRegistryImpl = new ConfigRegistryImpl(null, null,
+ configRegistryImpl = new ConfigRegistryImpl(null,
ManagementFactory.getPlatformMBeanServer());
Field field = configRegistryImpl.getClass().getDeclaredField(
"baseJMXRegistrator");
public TransactionJMXRegistrator create() {
return baseJMXRegistrator.createTransactionJMXRegistrator(transactionName123);
}
- });
+ }, currentlyRegisteredFactories);
ServiceReferenceWritableRegistry writableRegistry = ServiceReferenceRegistryImpl.createSRWritableRegistry(
ServiceReferenceRegistryImpl.createInitialSRLookupRegistry(), txLookupRegistry, currentlyRegisteredFactories);
--- /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.manager.testingservices.threadpool;
+
+import org.opendaylight.yangtools.yang.binding.annotations.ModuleQName;
+
+@ModuleQName(namespace = "namespace", revision = "revision", name = "name")
+public abstract class AbstractTestingFixedThreadPoolModuleFactory {
+}
import java.util.HashSet;
import java.util.Set;
-public class TestingFixedThreadPoolModuleFactory implements ModuleFactory {
+public class TestingFixedThreadPoolModuleFactory extends AbstractTestingFixedThreadPoolModuleFactory implements ModuleFactory {
public static final String NAME = "fixed";
private static Set<Class<? extends AbstractServiceInterface>> ifc = Collections.unmodifiableSet(Sets.newHashSet(
*/
package org.opendaylight.controller.config.manager.testingservices.threadpool.test;
+import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import static org.junit.Assert.assertEquals;
}
+ @Test
+ public void testQNames() {
+ Set<String> availableModuleFactoryQNames = configRegistryClient.getAvailableModuleFactoryQNames();
+ String expected = "(namespace?revision=revision)name";
+ assertEquals(Sets.newHashSet(expected), availableModuleFactoryQNames);
+ }
+
}
}
private void checkFileConsistency(){
- checkState(inCapabilities, "File {} is missing delimiters in this order: {}", fileNameForReporting,
+ checkState(inCapabilities, "File %s is missing delimiters in this order: %s", fileNameForReporting,
Arrays.asList(DirectoryPersister.MODULES_START,
DirectoryPersister.SERVICES_START,
DirectoryPersister.CAPABILITIES_START));
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>config-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.3-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>config-persister-directory-xml-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-file-xml-adapter</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.persistence</groupId>
+ <artifactId>org.eclipse.persistence.moxy</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.persistence</groupId>
+ <artifactId>org.eclipse.persistence.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
+ <plugin>
+ <groupId>org.codehaus.groovy.maven</groupId>
+ <artifactId>gmaven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>execute</goal>
+ </goals>
+ <configuration>
+ <source>
+ System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
+ </source>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
+ </Fragment-Host>
+ <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
+ </Provide-Capability>
+ <Import-Package>
+ com.google.common.base,
+ com.google.common.io,
+ org.apache.commons.io,
+ org.opendaylight.controller.config.persist.api,
+ org.slf4j,
+ 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,
+ </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.xml;
+
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+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 java.util.SortedSet;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class XmlDirectoryPersister implements Persister {
+ private static final Logger logger = LoggerFactory.getLogger(XmlDirectoryPersister.class);
+
+ private final File storage;
+
+ public XmlDirectoryPersister(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);
+ ConfigSnapshotHolder h = fromXmlSnapshot(file);
+ result.add(h);
+ }
+ return result;
+ }
+
+ private ConfigSnapshotHolder fromXmlSnapshot(File file) {
+ try {
+ JAXBContext jaxbContext = JAXBContext.newInstance(ConfigSnapshot.class);
+ Unmarshaller um = jaxbContext.createUnmarshaller();
+
+ return asHolder((ConfigSnapshot) um.unmarshal(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);
+ }
+ }
+
+ private ConfigSnapshotHolder asHolder(final ConfigSnapshot unmarshalled) {
+ return new ConfigSnapshotHolder() {
+ @Override
+ public String getConfigSnapshot() {
+ return unmarshalled.getConfigSnapshot();
+ }
+
+ @Override
+ public SortedSet<String> getCapabilities() {
+ return unmarshalled.getCapabilities();
+ }
+
+ @Override
+ public String toString() {
+ return unmarshalled.toString();
+ }
+ };
+ }
+
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("XmlDirectoryPersister{");
+ sb.append("storage=").append(storage);
+ sb.append('}');
+ return sb.toString();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.persist.storage.directory.xml;
+
+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 XmlDirectoryStorageAdapter implements StorageAdapter {
+ private static final Logger logger = LoggerFactory.getLogger(XmlDirectoryStorageAdapter.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 XmlDirectoryPersister(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.xml;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.SortedSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class DirectoryStorageAdapterTest {
+ XmlDirectoryPersister tested;
+
+ @Test
+ public void testEmptyDirectory() throws Exception {
+ File folder = new File("target/emptyFolder");
+ folder.mkdir();
+ tested = new XmlDirectoryPersister((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 = new XmlDirectoryPersister((folder));
+ List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
+ assertEquals(1, results.size());
+ ConfigSnapshotHolder result = results.get(0);
+ assertResult(result, "<config>1</config>", "cap1&rev", "cap2", "capa a");
+ }
+
+ private void assertResult(ConfigSnapshotHolder result, String s, String... caps) {
+ assertEquals(s, result.getConfigSnapshot().replaceAll("\\s", ""));
+ int i = 0;
+ for (String capFromSnapshot : result.getCapabilities()) {
+ assertEquals(capFromSnapshot, caps[i++]);
+ }
+ }
+
+ @Test
+ public void testTwoFiles() throws Exception {
+ File folder = getFolder("twoFiles");
+ tested = new XmlDirectoryPersister((folder));
+ 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");
+
+ }
+
+}
--- /dev/null
+<snapshot>
+ <required-capabilities>
+ <capability>cap1&rev</capability>
+ <capability>cap2</capability>
+ <capability>capa a</capability>
+ </required-capabilities>
+ <configuration>
+ <config>1</config>
+ </configuration>
+</snapshot>
\ No newline at end of file
--- /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
+<snapshot>
+ <required-capabilities>
+ <capability>cap1-b</capability>
+ <capability>cap2-b</capability>
+ <capability>capa a-b</capability>
+ </required-capabilities>
+ <configuration>
+ <config>2</config>
+ </configuration>
+</snapshot>
\ 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.3-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>config-persister-file-xml-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>org.eclipse.persistence</groupId>
+ <artifactId>org.eclipse.persistence.moxy</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.persistence</groupId>
+ <artifactId>org.eclipse.persistence.core</artifactId>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
+ <plugin>
+ <groupId>org.codehaus.groovy.maven</groupId>
+ <artifactId>gmaven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>execute</goal>
+ </goals>
+ <configuration>
+ <source>
+ System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
+ </source>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
+ </Fragment-Host>
+ <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
+ </Provide-Capability>
+ </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.xml;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.config.persist.api.PropertiesProvider;
+import org.opendaylight.controller.config.persist.api.StorageAdapter;
+import org.opendaylight.controller.config.persist.storage.file.xml.model.Config;
+import org.opendaylight.controller.config.persist.storage.file.xml.model.ConfigSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * StorageAdapter that stores configuration in an xml file.
+ */
+public class XmlFileStorageAdapter implements StorageAdapter, Persister {
+ private static final Logger logger = LoggerFactory.getLogger(XmlFileStorageAdapter.class);
+
+ public static final String FILE_STORAGE_PROP = "fileStorage";
+ public static final String NUMBER_OF_BACKUPS = "numberOfBackups";
+
+ 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
+ public void setFileStorage(File storage) {
+ this.storage = storage;
+ }
+
+ @VisibleForTesting
+ public 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;
+ }
+
+ @Override
+ public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
+ Preconditions.checkNotNull(storage, "Storage file is null");
+
+ Config cfg = Config.fromXml(storage);
+ cfg.addConfigSnapshot(ConfigSnapshot.fromConfigSnapshot(holder), numberOfStoredBackups);
+ cfg.toXml(storage);
+ }
+
+ @Override
+ public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
+ Preconditions.checkNotNull(storage, "Storage file is null");
+
+ if (!storage.exists()) {
+ return Collections.emptyList();
+ }
+
+ Optional<ConfigSnapshot> lastSnapshot = Config.fromXml(storage).getLastSnapshot();
+
+ if (lastSnapshot.isPresent())
+ return Lists.newArrayList(toConfigSnapshot(lastSnapshot.get()));
+ else
+ return Collections.emptyList();
+ }
+
+
+ public ConfigSnapshotHolder toConfigSnapshot(final ConfigSnapshot configSnapshot) {
+ return new ConfigSnapshotHolder() {
+ @Override
+ public String getConfigSnapshot() {
+ return configSnapshot.getConfigSnapshot();
+ }
+
+ @Override
+ public SortedSet<String> getCapabilities() {
+ return configSnapshot.getCapabilities();
+ }
+
+ @Override
+ public String toString() {
+ return configSnapshot.toString();
+ }
+ };
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String toString() {
+ return "XmlFileStorageAdapter [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.xml.model;
+
+import javax.xml.bind.ValidationEventHandler;
+import javax.xml.bind.annotation.DomHandler;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+class CapabilityHandler implements DomHandler<String, StreamResult> {
+
+ private static final String START_TAG = "<capability>";
+ private static final String END_TAG = "</capability>";
+
+ private StringWriter xmlWriter = new StringWriter();
+
+ public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
+ xmlWriter.getBuffer().setLength(0);
+ return new StreamResult(xmlWriter);
+ }
+
+ public String getElement(StreamResult rt) {
+ String xml = rt.getWriter().toString();
+ int beginIndex = xml.indexOf(START_TAG) + START_TAG.length();
+ int endIndex = xml.indexOf(END_TAG);
+ return xml.substring(beginIndex, endIndex);
+ }
+
+ public Source marshal(String n, ValidationEventHandler errorHandler) {
+ try {
+ String xml = START_TAG + n.trim() + END_TAG;
+ StringReader xmlReader = new StringReader(xml);
+ return new StreamSource(xmlReader);
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.persist.storage.file.xml.model;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+@XmlRootElement(name = "persisted-snapshots")
+public final class Config {
+
+ private List<ConfigSnapshot> snapshots;
+
+ Config(List<ConfigSnapshot> snapshots) {
+ this.snapshots = snapshots;
+ }
+
+ public Config() {
+ this.snapshots = Lists.newArrayList();
+ }
+
+ @XmlElement(name = "snapshot")
+ @XmlElementWrapper(name = "snapshots")
+ public List<ConfigSnapshot> getSnapshots() {
+ return snapshots;
+ }
+
+ public void setSnapshots(List<ConfigSnapshot> snapshots) {
+ this.snapshots = snapshots;
+ }
+
+ public void toXml(File to) {
+ try {
+
+ // TODO Moxy has to be used instead of default jaxb impl due to a bug
+ // default implementation has a bug that prevents from serializing xml in a string
+ JAXBContext jaxbContext = org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(new Class[]{Config.class}, null);
+
+ Marshaller marshaller = jaxbContext.createMarshaller();
+
+ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
+
+ marshaller.marshal(this, to);
+ } catch (JAXBException e) {
+ throw new PersistException("Unable to persist configuration", e);
+ }
+ }
+
+ public static Config fromXml(File from) {
+ if(isEmpty(from))
+ return new Config();
+
+ try {
+ JAXBContext jaxbContext = JAXBContext.newInstance(Config.class);
+ Unmarshaller um = jaxbContext.createUnmarshaller();
+
+ return (Config) um.unmarshal(from);
+ } catch (JAXBException e) {
+ throw new PersistException("Unable to restore configuration", e);
+ }
+ }
+
+ private static boolean isEmpty(File from) {
+ return from.length() == 0 || isBlank(from);
+ }
+
+ private static boolean isBlank(File from) {
+ try {
+ return StringUtils.isBlank(Files.toString(from, Charsets.UTF_8));
+ } catch (IOException e) {
+ throw new IllegalStateException("Unexpected error reading file" + from, e);
+ }
+ }
+
+ public Optional<ConfigSnapshot> getLastSnapshot() {
+ ConfigSnapshot last = Iterables.getLast(snapshots, null);
+ return last == null ? Optional.<ConfigSnapshot>absent() : Optional.of(last);
+ }
+
+ public void addConfigSnapshot(ConfigSnapshot snap, int numberOfStoredBackups) {
+ if(shouldReplaceLast(numberOfStoredBackups) && snapshots.isEmpty() == false) {
+ snapshots.remove(0);
+ }
+ snapshots.add(snap);
+ }
+
+ private boolean shouldReplaceLast(int numberOfStoredBackups) {
+ return numberOfStoredBackups == snapshots.size();
+ }
+}
--- /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.xml.model;
+
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+
+import javax.xml.bind.annotation.XmlAnyElement;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.SortedSet;
+
+@XmlRootElement(name = "snapshot")
+public class ConfigSnapshot {
+
+ private String configSnapshot;
+ private SortedSet<String> capabilities;
+
+ ConfigSnapshot(String configXml, SortedSet<String> capabilities) {
+ this.configSnapshot = configXml;
+ this.capabilities = capabilities;
+ }
+
+ public ConfigSnapshot() {
+ }
+
+ public static ConfigSnapshot fromConfigSnapshot(ConfigSnapshotHolder cfg) {
+ return new ConfigSnapshot(cfg.getConfigSnapshot(), cfg.getCapabilities());
+ }
+
+ @XmlAnyElement(SnapshotHandler.class)
+ public String getConfigSnapshot() {
+ return configSnapshot;
+ }
+
+ public void setConfigSnapshot(String configSnapshot) {
+ this.configSnapshot = configSnapshot;
+ }
+
+ @XmlElement(name = "capability")
+ @XmlElementWrapper(name = "required-capabilities")
+ public SortedSet<String> getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(SortedSet<String> capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("ConfigSnapshot{");
+ sb.append("configSnapshot='").append(configSnapshot).append('\'');
+ sb.append(", capabilities=").append(capabilities);
+ 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.file.xml.model;
+
+final class PersistException extends RuntimeException {
+
+ public PersistException(String s, Exception e) {
+ super(s, e);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.persist.storage.file.xml.model;
+
+import javax.xml.bind.ValidationEventHandler;
+import javax.xml.bind.annotation.DomHandler;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+class SnapshotHandler implements DomHandler<String, StreamResult> {
+
+ private static final String START_TAG = "<configuration>";
+ private static final String END_TAG = "</configuration>";
+
+ private StringWriter xmlWriter = new StringWriter();
+
+ public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
+ xmlWriter.getBuffer().setLength(0);
+ return new StreamResult(xmlWriter);
+ }
+
+ public String getElement(StreamResult rt) {
+ String xml = rt.getWriter().toString();
+ int beginIndex = xml.indexOf(START_TAG) + START_TAG.length();
+ int endIndex = xml.indexOf(END_TAG);
+ return xml.substring(beginIndex, endIndex);
+ }
+
+ public Source marshal(String n, ValidationEventHandler errorHandler) {
+ try {
+ String xml = START_TAG + n.trim() + END_TAG;
+ StringReader xmlReader = new StringReader(xml);
+ return new StreamSource(xmlReader);
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.persist.storage.file.xml;
+
+import com.google.common.base.Charsets;
+import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+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 testFileAdapter() throws Exception {
+ XmlFileStorageAdapter storage = new XmlFileStorageAdapter();
+ 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);
+
+ assertEquals(27, com.google.common.io.Files.readLines(file, Charsets.UTF_8).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());
+
+ storage = new XmlFileStorageAdapter();
+ storage.setFileStorage(file);
+ storage.setNumberOfBackups(Integer.MAX_VALUE);
+
+ List<ConfigSnapshotHolder> last = storage.loadLastConfigs();
+ Assert.assertEquals(createCaps(), last.get(0).getCapabilities());
+ }
+
+ private SortedSet<String> createCaps() {
+ SortedSet<String> caps = new TreeSet<>();
+
+ caps.add("cap1" + i);
+ caps.add("cap2" + i);
+ caps.add("urn:opendaylight:params:xml:ns:yang:controller:netty?module=netty&revision=2013-11-19" + i);
+ caps.add("capaaaa as dasfasdf s2" + i);
+ return caps;
+ }
+
+ @Test
+ public void testFileAdapterOneBackup() throws Exception {
+ XmlFileStorageAdapter storage = new XmlFileStorageAdapter();
+ 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);
+
+ assertEquals(16, com.google.common.io.Files.readLines(file, Charsets.UTF_8).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 {
+ XmlFileStorageAdapter storage = new XmlFileStorageAdapter();
+ 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);
+
+ List<String> readLines = com.google.common.io.Files.readLines(file, Charsets.UTF_8);
+ assertEquals(27, 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;
+ XmlFileStorageAdapter storage = new XmlFileStorageAdapter();
+ storage.setFileStorage(file);
+
+ List<ConfigSnapshotHolder> elementOptional = storage.loadLastConfigs();
+ assertThat(elementOptional.size(), is(0));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNoProperties() throws Exception {
+ XmlFileStorageAdapter storage = new XmlFileStorageAdapter();
+ storage.loadLastConfigs();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNoProperties2() throws Exception {
+ XmlFileStorageAdapter storage = new XmlFileStorageAdapter();
+ 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>";
+ }
+
+}
*/
package org.opendaylight.controller.config.util;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.ReflectionException;
-
-import org.opendaylight.controller.config.api.ConflictingVersionException;
-import org.opendaylight.controller.config.api.ValidationException;
-import org.opendaylight.controller.config.api.jmx.CommitStatus;
-import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
-import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
public class ConfigRegistryJMXClient implements ConfigRegistryClient {
private final ConfigRegistryMXBean configRegistryMXBeanProxy;
}
}
+ @Override
+ public Set<String> getAvailableModuleFactoryQNames() {
+ return configRegistryMXBeanProxy.getAvailableModuleFactoryQNames();
+ }
}
+ attrName + " for " + on, e);
}
}
+
+ @Override
+ public Set<String> getAvailableModuleFactoryQNames() {
+ return configTransactionControllerMXBeanProxy.getAvailableModuleFactoryQNames();
+ }
}
public String getServiceInterfaceName(String namespace, String localName) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public Set<String> getAvailableModuleFactoryQNames() {
+ throw new UnsupportedOperationException();
+ }
}
public boolean removeServiceReferences(ObjectName objectName) throws InstanceNotFoundException {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public Set<String> getAvailableModuleFactoryQNames() {
+ throw new UnsupportedOperationException();
+ }
}
<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>yang-store-api</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>yang-test-plugin</module>
</modules>
import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
import org.opendaylight.controller.config.spi.Module;
import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
private final String globallyUniqueName, moduleInstanceType;
private final List<String> providedServices;
+ private final ModuleMXBeanEntry mbe;
public AbstractFactoryTemplate(Header header, String packageName,
- String abstractFactoryName, String globallyUniqueName,
- String moduleInstanceType, List<Field> fields,
- List<String> providedServices) {
+ String abstractFactoryName, String globallyUniqueName,
+ String moduleInstanceType, List<Field> fields,
+ List<String> providedServices, ModuleMXBeanEntry mbe) {
super(header, packageName, abstractFactoryName, Collections
.<String> emptyList(), implementedIfcs, fields, Collections
.<MethodDefinition> emptyList(), true, false, Collections
this.globallyUniqueName = globallyUniqueName;
this.moduleInstanceType = moduleInstanceType;
this.providedServices = providedServices;
+ this.mbe = mbe;
}
public String getGloballyUniqueName() {
return "factory_abs_template.ftl";
}
+ public ModuleMXBeanEntry getMbe() {
+ return mbe;
+ }
}
sieTemplate.getAnnotations().add(
Annotation.createDescriptionAnnotation(sie
.getNullableDescription()));
- sieTemplate.getAnnotations().add(Annotation.createSieAnnotation(sie.getQName(), sie.getExportedOsgiClassName
- ()));
+ sieTemplate.getAnnotations().addAll(Annotation.createSieAnnotations(sie));
return sieTemplate;
}
mbe.getPackageName(), mbe.getAbstractFactoryName(),
mbe.getGloballyUniqueName(), mbe.getFullyQualifiedName(mbe
.getStubModuleName()), attrProcessor.getFields(),
- Lists.newArrayList(transformed));
+ Lists.newArrayList(transformed), mbe);
}
public static AbstractModuleTemplate abstractModuleTemplateFromMbe(
*/
package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import org.opendaylight.controller.config.api.annotations.Description;
import org.opendaylight.controller.config.api.annotations.RequireInterface;
import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+import org.opendaylight.yangtools.yang.binding.annotations.ModuleQName;
import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
-import org.opendaylight.yangtools.yang.common.QName;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
Lists.newArrayList(new Parameter("value", q(description))));
}
- public static Annotation createSieAnnotation(QName qname,
- String exportedClassName) {
- Preconditions.checkNotNull(qname,
+ public static Collection<Annotation> createSieAnnotations(ServiceInterfaceEntry sie){
+
+ String exportedClassName = sie.getExportedOsgiClassName();
+ Preconditions.checkNotNull(sie.getQName(),
"Cannot create annotation from null qname");
Preconditions.checkNotNull(exportedClassName,
"Cannot create annotation from null exportedClassName");
+ List<Annotation> result = new ArrayList<>();
+ {
+ List<Parameter> params = Lists.newArrayList(new Parameter("value", q(sie.getQName().toString())));
+ params.add(new Parameter("osgiRegistrationType", exportedClassName + ".class"));
- List<Parameter> params = Lists.newArrayList(new Parameter("value", q(qname.toString())));
- params.add(new Parameter("osgiRegistrationType", exportedClassName + ".class"));
+ params.add(new Parameter("namespace", q(sie.getQName().getNamespace().toString())));
+ params.add(new Parameter("revision", q(sie.getQName().getFormattedRevision())));
+ params.add(new Parameter("localName", q(sie.getQName().getLocalName())));
- params.add(new Parameter("namespace", q(qname.getNamespace().toString())));
- params.add(new Parameter("revision", q(qname.getFormattedRevision())));
- params.add(new Parameter("localName", q(qname.getLocalName())));
+ Annotation sieAnnotation = new Annotation(ServiceInterfaceAnnotation.class.getCanonicalName(), params);
+ result.add(sieAnnotation);
- return new Annotation(
- ServiceInterfaceAnnotation.class.getCanonicalName(), params);
+ }
+ {
+ List<Parameter> params = new ArrayList<>();
+ params.add(new Parameter("namespace", q(sie.getYangModuleQName().getNamespace().toString())));
+ params.add(new Parameter("revision", q(sie.getYangModuleQName().getFormattedRevision())));
+ params.add(new Parameter("name", q(sie.getYangModuleQName().getLocalName())));
+
+ Annotation moduleQNameAnnotation = new Annotation(ModuleQName.class.getCanonicalName(), params);
+ result.add(moduleQNameAnnotation);
+ }
+ return result;
}
public static Annotation createRequireIfcAnnotation(
package ${packageName};
<@javadocD object=javadoc/>
+@org.opendaylight.yangtools.yang.binding.annotations.ModuleQName(namespace="${mbe.getYangModuleQName().getNamespace().toString()}",revision="${mbe.getYangModuleQName().getFormattedRevision()}",name="${mbe.getYangModuleQName().getLocalName()}")
<@typeDeclarationD object=typeDeclaration/>
{
<artifactId>commons-lang3</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-type-provider</artifactId>
+ </dependency>
</dependencies>
<build>
private final Map<String, QName> providedServices;
private Collection<RuntimeBeanEntry> runtimeBeans;
+ private final QName yangModuleQName;
public ModuleMXBeanEntry(IdentitySchemaNode id,
Map<String, AttributeIfc> yangToAttributes, String packageName,
Map<String, QName> providedServices2, String javaNamePrefix,
- String namespace, Collection<RuntimeBeanEntry> runtimeBeans) {
+ String namespace, Collection<RuntimeBeanEntry> runtimeBeans,
+ QName yangModuleQName) {
this.globallyUniqueName = id.getQName().getLocalName();
this.yangToAttributes = yangToAttributes;
this.nullableDescription = id.getDescription();
this.namespace = checkNotNull(namespace);
this.providedServices = Collections.unmodifiableMap(providedServices2);
this.runtimeBeans = runtimeBeans;
+ this.yangModuleQName = yangModuleQName;
}
public String getMXBeanInterfaceName() {
moduleIdentity, yangToAttributes, packageName,
providedServices, javaNamePrefix, currentModule
.getNamespace().toString(),
- runtimeBeans);
+ runtimeBeans,
+ ModuleUtil.getQName(currentModule));
moduleMXBeanEntry.setYangModuleName(currentModule
.getName());
moduleMXBeanEntry
return nullableDescription;
}
+ public QName getYangModuleQName() {
+ return yangModuleQName;
+ }
+
@Override
public String toString() {
return "ModuleMXBeanEntry{" + "globallyUniqueName='"
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+public class ModuleUtil {
+
+ public static QName getQName(Module currentModule){
+ return new QName(currentModule.getNamespace(), currentModule.getRevision(), currentModule.getName());
+ }
+}
private final String exportedOsgiClassName;
private final QName qName;
private final String nullableDescription, packageName, typeName;
+ private final QName yangModuleQName;
- private ServiceInterfaceEntry(IdentitySchemaNode id, String packageName) {
- this(Optional.<ServiceInterfaceEntry> absent(), id, packageName);
+ private ServiceInterfaceEntry(IdentitySchemaNode id, String packageName, QName yangModuleQName) {
+ this(Optional.<ServiceInterfaceEntry> absent(), id, packageName, yangModuleQName);
}
private ServiceInterfaceEntry(Optional<ServiceInterfaceEntry> base,
- IdentitySchemaNode id, String packageName) {
+ IdentitySchemaNode id, String packageName, QName yangModuleQName) {
checkNotNull(base);
this.maybeBaseCache = base;
List<UnknownSchemaNode> unknownSchemaNodes = id.getUnknownSchemaNodes();
nullableDescription = id.getDescription();
typeName = getSimpleName(exportedOsgiClassName) + CLASS_NAME_SUFFIX;
this.packageName = packageName;
+ this.yangModuleQName = yangModuleQName;
}
private static final String getSimpleName(String fullyQualifiedName) {
* @return Map of QNames as keys and ServiceInterfaceEntry instances as
* values
*/
- public static Map<QName, ServiceInterfaceEntry> create(Module module,
+ public static Map<QName, ServiceInterfaceEntry> create(Module currentModule,
String packageName) {
logger.debug("Generating ServiceInterfaces from {} to package {}",
- module.getNamespace(), packageName);
+ currentModule.getNamespace(), packageName);
Map<IdentitySchemaNode, ServiceInterfaceEntry> identitiesToSIs = new HashMap<>();
Set<IdentitySchemaNode> notVisited = new HashSet<>(
- module.getIdentities());
+ currentModule.getIdentities());
int lastSize = notVisited.size() + 1;
while (notVisited.size() > 0) {
if (notVisited.size() == lastSize) {
} else if (identity.getBaseIdentity().getQName()
.equals(SERVICE_TYPE_Q_NAME)) {
// this is a base type
- created = new ServiceInterfaceEntry(identity, packageName);
+ created = new ServiceInterfaceEntry(identity, packageName, ModuleUtil.getQName(currentModule));
} else {
ServiceInterfaceEntry foundBase = identitiesToSIs
.get(identity.getBaseIdentity());
// derived type, did we convert the parent?
if (foundBase != null) {
created = new ServiceInterfaceEntry(
- Optional.of(foundBase), identity, packageName);
+ Optional.of(foundBase), identity, packageName, ModuleUtil.getQName(currentModule));
}
}
if (created != null) {
- created.setYangModuleName(module.getName());
+ created.setYangModuleName(currentModule.getName());
// TODO how to get local name
created.setYangModuleLocalname(identity.getQName()
.getLocalName());
return typeName;
}
+ public QName getYangModuleQName() {
+ return yangModuleQName;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o)
public String getJMXParamForBaseType(TypeDefinition<?> baseType) {
return typeProvider.getConstructorPropertyName(baseType);
}
+
+ public String getJMXParamForUnionInnerType(TypeDefinition<?> unionInnerType) {
+ return typeProvider.getParamNameFromType(unionInnerType);
+ }
}
*/
package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+import com.google.common.base.Preconditions;
import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
import org.opendaylight.yangtools.sal.binding.model.api.Type;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
import javax.management.openmbean.ArrayType;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
+import java.util.Arrays;
+import java.util.List;
public class JavaAttribute extends AbstractAttribute implements TypedAttribute {
+ public static final String DESCRIPTION_OF_VALUE_ATTRIBUTE_FOR_UNION = "valueOfArtificialUnionProperty";
+
private final Type type;
private final String nullableDescription, nullableDefault, nullableDefaultWrappedForCode;
private final TypeProviderWrapper typeProviderWrapper;
this.nullableDescription = leaf.getDescription();
}
+ public boolean isUnion() {
+ TypeDefinition<?> base = getBaseType(typeProviderWrapper, typeDefinition);
+ return base instanceof UnionTypeDefinition;
+ }
+
public TypeDefinition<?> getTypeDefinition() {
return typeDefinition;
}
return getArrayType();
} else if (isEnum(baseType)) {
return getSimpleType(baseType);
- } else if (isDerivedType(baseType)) {
+ } else if (isUnion()) {
+ return getCompositeTypeForUnion(baseTypeDefinition);
+ } else if (isDerivedType(baseType, getType())) {
return getCompositeType(baseType, baseTypeDefinition);
}
return getSimpleType(getType());
}
+ private OpenType<?> getCompositeTypeForUnion(TypeDefinition<?> baseTypeDefinition) {
+ Preconditions.checkArgument(baseTypeDefinition instanceof UnionTypeDefinition,
+ "Expected %s instance but was %s", UnionTypeDefinition.class, baseTypeDefinition);
+
+ List<TypeDefinition<?>> types = ((UnionTypeDefinition) baseTypeDefinition).getTypes();
+
+ String[] itemNames = new String[types.size()+1];
+ OpenType<?>[] itemTypes = new OpenType[itemNames.length];
+
+ addArtificialPropertyToUnionCompositeType(baseTypeDefinition, itemNames, itemTypes);
+
+ String description = getNullableDescription() == null ? getAttributeYangName() : getNullableDescription();
+
+ int i = 1;
+ for (TypeDefinition<?> innerTypeDefinition : types) {
+
+ Type innerType = typeProviderWrapper.getType(innerTypeDefinition, innerTypeDefinition);
+
+ TypeDefinition<?> baseInnerTypeDefinition = getBaseType(typeProviderWrapper, innerTypeDefinition);
+ Type innerTypeBaseType = typeProviderWrapper.getType(baseInnerTypeDefinition, baseInnerTypeDefinition);
+
+ OpenType<?> innerCompositeType;
+
+ if(isDerivedType(innerTypeBaseType, innerType)) {
+ innerCompositeType = getCompositeType(innerTypeBaseType, baseInnerTypeDefinition);
+ } else {
+ innerCompositeType = SimpleTypeResolver.getSimpleType(innerType);
+ }
+
+ itemNames[i] = typeProviderWrapper.getJMXParamForUnionInnerType(innerTypeDefinition);
+ itemTypes[i++] = innerCompositeType;
+ }
+
+ String[] descriptions = Arrays.copyOf(itemNames, itemNames.length);
+ descriptions[0] = DESCRIPTION_OF_VALUE_ATTRIBUTE_FOR_UNION;
+
+ try {
+ return new CompositeType(getUpperCaseCammelCase(), description, itemNames, descriptions, itemTypes);
+ } catch (OpenDataException e) {
+ throw new RuntimeException("Unable to create " + CompositeType.class + " with inner elements "
+ + Arrays.toString(itemTypes), e);
+ }
+ }
+
+ public static final Class<Character> TYPE_OF_ARTIFICIAL_UNION_PROPERTY = char.class;
+
+ private void addArtificialPropertyToUnionCompositeType(TypeDefinition<?> baseTypeDefinition, String[] itemNames, OpenType<?>[] itemTypes) {
+ String artificialPropertyName = typeProviderWrapper.getJMXParamForBaseType(baseTypeDefinition);
+ itemNames[0] = artificialPropertyName;
+
+ OpenType<?> artificialPropertyType = getArrayOpenTypeForSimpleType(TYPE_OF_ARTIFICIAL_UNION_PROPERTY.getName(),
+ SimpleTypeResolver.getSimpleType(TYPE_OF_ARTIFICIAL_UNION_PROPERTY.getName()));
+ itemTypes[0] = artificialPropertyType;
+ }
+
private boolean isEnum(Type baseType) {
return baseType.getFullyQualifiedName().equals(Enum.class.getName());
}
throw new RuntimeException("Unable to create " + CompositeType.class + " with inner element of type "
+ itemTypes, e);
}
-
}
private OpenType<?> getArrayType() {
String innerTypeFullyQName = getInnerType(getType());
SimpleType<?> innerSimpleType = SimpleTypeResolver.getSimpleType(innerTypeFullyQName);
+ return getArrayOpenTypeForSimpleType(innerTypeFullyQName, innerSimpleType);
+ }
+
+ private OpenType<?> getArrayOpenTypeForSimpleType(String innerTypeFullyQName, SimpleType<?> innerSimpleType) {
try {
ArrayType<Object> arrayType = isPrimitive(innerTypeFullyQName) ? new ArrayType<>(innerSimpleType, true)
: new ArrayType<>(1, innerSimpleType);
return type.getName().endsWith("[]");
}
- private boolean isDerivedType(Type baseType) {
- return baseType.equals(getType()) == false;
+ private boolean isDerivedType(Type baseType, Type currentType) {
+ return baseType.equals(currentType) == false;
}
private static String getInnerType(Type type) {
JAVA_TYPE_TO_SIMPLE_TYPE.put(Date.class.getName(), SimpleType.DATE);
JAVA_TYPE_TO_SIMPLE_TYPE.put(Double.class.getName(), SimpleType.DOUBLE);
JAVA_TYPE_TO_SIMPLE_TYPE.put(double.class.getName(), SimpleType.DOUBLE);
+
+ JAVA_TYPE_TO_SIMPLE_TYPE.put(char.class.getName(), SimpleType.CHARACTER);
}
}
--- /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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.test.types.rev131127;
+
+
+/**
+**/
+public class UnionTestBuilder {
+
+ public static UnionTest getDefaultInstance(String defaultValue) {
+ try {
+ int i = Integer.valueOf(defaultValue);
+ return new UnionTest(new ExtendTwice(i));
+ } catch (NumberFormatException e) {
+ return new UnionTest(defaultValue);
+ }
+ }
+
+}
default 127.0.0.1;
}
- leaf ip {
- type inet:ip-address;
- // TODO defaults for union default 0:0:0:0:0:0:0:1;
- }
}
leaf as-number {
default ONE;
}
+ leaf ip {
+ type inet:ip-address;
+ default 0:0:0:0:0:0:0:1;
+ }
+
+ leaf union-test-attr {
+ type tt:unionTest;
+ default 456;
+ }
+
leaf sleep-factor {
type decimal64 {
fraction-digits 2;
}
}
+ typedef unionTest {
+ type union {
+ type string;
+ type uint32;
+ type extend-twice;
+ }
+ }
+
}
<artifactId>config-persister-file-adapter</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
+ <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>
<!-- Netconf -->
<dependency>
</dependencies>
</profile>
<profile>
- <!-- sanitytests are only enabled with this profile -->
<id>integrationtests</id>
<activation>
<activeByDefault>false</activeByDefault>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-invoker-plugin</artifactId>
- <version>1.5</version>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.8</version>
+ <executions>
+ <execution>
+ <id>copy</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ </execution>
+ </executions>
<configuration>
- <debug>false</debug>
- <projectsDirectory>../sanitytest</projectsDirectory>
- <pomIncludes>
- <pomInclude>pom.xml</pomInclude>
- </pomIncludes>
- <streamLogs>true</streamLogs>
- <noLog>true</noLog>
- <goals>
- <goal>clean</goal>
- <goal>verify</goal>
- </goals>
- </configuration>
- <executions>
- <execution>
- <id>integration-test</id>
- <goals>
- <goal>install</goal>
- <goal>run</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sanitytest</artifactId>
+ <version>${controller.version}</version>
+ <type>jar</type>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <executions>
+ <execution>
+ <id>sanity-test</id>
+ <phase>package</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <executable>${java.home}/bin/java</executable>
+ <arguments>
+ <argument>-cp</argument>
+ <argument>./target/dependency/*</argument>
+ <argument>org.opendaylight.controller.distribution.Sanity</argument>
+ </arguments>
+ <environmentVariables>
+ <JAVA_HOME>
+ ${java.home}
+ </JAVA_HOME>
+ </environmentVariables>
+ </configuration>
+ </plugin>
</plugins>
</build>
-
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>controller-maven-plugin</artifactId>
- <version>0.1.0-SNAPSHOT</version>
- </dependency>
- </dependencies>
</profile>
</profiles>
<version>${commons.httpclient.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sanitytest</artifactId>
+ <version>${controller.version}</version>
+ </dependency>
+
+
</dependencies>
</execution>
</executions>
</plugin>
-
</plugins>
</build>
-
</project>
--- /dev/null
+rem Inject the sanitytest jar as a controller plugin
+copy .\target\dependency\sanitytest*.jar .\target\distribution.opendaylight-osgipackage\opendaylight\plugins
+
+rem Store the current working directory in a variable so that we can get back to it later
+set cwd=%cd%
+
+rem Switch to the distribution folder
+cd .\target\distribution.opendaylight-osgipackage\opendaylight
+
+rem Run the controller
+cmd.exe /c run.bat
+
+rem Store the exit value of the controller in a variable
+set success=%ERRORLEVEL%
+
+rem Switch back to the directory from which this script was invoked
+cd %cwd%
+
+rem Remove the sanitytest jar from the plugins directory
+del .\target\distribution.opendaylight-osgipackage\opendaylight\plugins\sanitytest*.jar
+
+rem Exit using the exit code that we had captured earlier after running the controller
+exit /b %SUCCESS%
\ No newline at end of file
--- /dev/null
+# Inject the sanitytest jar as a controller plugin
+cp ./target/dependency/sanitytest*.jar ./target/distribution.opendaylight-osgipackage/opendaylight/plugins
+
+# Store the current working directory in a variable so that we can get back to it later
+cwd=`pwd`
+
+# Switch to the distribution folder
+cd ./target/distribution.opendaylight-osgipackage/opendaylight/
+
+# Run the controller
+./run.sh
+
+# Store the exit value of the controller in a variable
+success=`echo $?`
+
+# Switch back to the directory from which this script was invoked
+cd $cwd
+
+# Remove the sanitytest jar from the plugins directory
+rm ./target/distribution.opendaylight-osgipackage/opendaylight/plugins/sanitytest*.jar
+
+# Exit using the exit code that we had captured earlier after running the controller
+exit $success
+
<exclude>com.sun.jersey:jersey-json</exclude>
<exclude>com.sun.jersey:jersey-server</exclude>
<exclude>org.opendaylight.controller:logging.bridge</exclude>
+ <exclude>org.opendaylight.controller:sanitytest</exclude>
</excludes>
<outputFileNameMapping>
${artifact.groupId}.${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}
netconf.config.persister.1.properties.directoryStorage=configuration/initial/
netconf.config.persister.1.readonly=true
-netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
-netconf.config.persister.2.properties.fileStorage=configuration/current/controller.currentconfig.txt
+#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
ECHO %RUN_CMD%
if "%startEnabled%" NEQ "" (
- START /B cmd /C CALL %RUN_CMD%
+ START /B cmd /C CALL %RUN_CMD% > %basedir%\logs\controller.out 2>&1
ECHO Running controller in the background.
) else (
%RUN_CMD%
<artifactId>sanitytest</artifactId>
<version>0.4.1-SNAPSHOT</version>
- <packaging>jar</packaging>
-
- <properties>
- <distro.dir>${project.basedir}/../opendaylight/target/distribution.opendaylight-osgipackage/opendaylight</distro.dir>
- <distro.script>${distro.dir}/run.bat</distro.script>
- <!-- the address is passed to both the controller & the test to establish the connection -->
- <sanitytest.bind.address>127.0.0.1</sanitytest.bind.address>
- <sanitytest.timeout>300</sanitytest.timeout>
- </properties>
-
- <profiles>
- <profile>
- <id>non-windows</id>
- <activation>
- <os>
- <family>!windows</family>
- </os>
- </activation>
- <properties>
- <distro.script>${distro.dir}/run.sh</distro.script>
- <distro.pid>/tmp/opendaylight.PID</distro.pid>
- </properties>
- </profile>
- </profiles>
-
+ <packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<scope>provided</scope>
</dependency>
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>org.ow2.chameleon.management</groupId>
- <artifactId>chameleon-mbeans</artifactId>
- <scope>test</scope>
- </dependency>
-
</dependencies>
-
<build>
<plugins>
<plugin>
- <!-- disable the install plugin -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-install-plugin</artifactId>
- <version>${install.plugin.version}</version>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${bundle.plugin.version}</version>
+ <extensions>true</extensions>
<configuration>
- <skip>true</skip>
+ <instructions>
+ <Export-Package>
+ org.opendaylight.controller.sanitytest
+ </Export-Package>
+ <Import-Package>
+ javax.xml.bind.annotation,
+ org.osgi.service.component,
+ org.slf4j,
+ org.eclipse.osgi.framework.console,
+ org.osgi.framework,
+ org.eclipse.osgi.baseadaptor,
+ org.eclipse.osgi.framework.adaptor,
+ org.osgi.framework.wiring
+ </Import-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.sanitytest.internal.Activator
+ </Bundle-Activator>
+ </instructions>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
</configuration>
- <executions>
- <execution>
- <id>default-install</id>
- <phase>none</phase>
- </execution>
- </executions>
</plugin>
-
- <plugin>
- <!-- ensure that the distro installation is already built -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-enforcer-plugin</artifactId>
- <version>${enforcer.plugin.version}</version>
- <executions>
- <execution>
- <id>enforce-files-exist</id>
- <phase>pre-integration-test</phase>
- <goals>
- <goal>enforce</goal>
- </goals>
- <configuration>
- <rules>
- <requireFilesExist>
- <files>
- <file>${distro.script}</file>
- </files>
- </requireFilesExist>
- </rules>
- <fail>true</fail>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>controller-maven-plugin</artifactId>
- <version>0.1.0-SNAPSHOT</version>
- <configuration>
- <controllerHome>${distro.dir}</controllerHome>
- <controllerHost>localhost</controllerHost>
- <controllerWebPort>8080</controllerWebPort>
- <controllerUsername>admin</controllerUsername>
- <controllerPassword>admin</controllerPassword>
- <controllerStartScriptName>${distro.script}</controllerStartScriptName>
- <pidFile>${distro.pid}</pidFile>
- </configuration>
- <executions>
- <execution>
- <!-- ensure controller is started in pre-integration phase -->
- <id>start-controller</id>
- <phase>pre-integration-test</phase>
- <configuration>
- <startArgs>
- <param>-start</param>
- <param>-jmx</param>
- <param>-Djava.rmi.server.hostname=${sanitytest.bind.address} </param>
- </startArgs>
- <warmupTimeSecs> 60 </warmupTimeSecs>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
- <execution>
- <!-- ensure controller is stopped in post-integration phase -->
- <id>stop-controller</id>
- <phase>post-integration-test</phase>
- <goals>
- <goal>stop</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <!-- run the test -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-failsafe-plugin</artifactId>
- <version>${failsafe.version}</version>
- <configuration>
- <systemPropertyVariables>
- <ctrl.home>${distro.dir}</ctrl.home>
- <ctrl.host>${sanitytest.bind.address}</ctrl.host>
- <ctrl.start.timeout>${sanitytest.timeout}</ctrl.start.timeout>
- </systemPropertyVariables>
- </configuration>
- </plugin>
-
</plugins>
-
-
</build>
</project>
--- /dev/null
+package org.opendaylight.controller.distribution;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Sanity {
+
+ static void copy(InputStream in, OutputStream out) throws IOException {
+ while (true) {
+ int c = in.read();
+ if (c == -1) break;
+ out.write((char)c);
+ }
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException {
+ String cwd = System.getProperty("user.dir");
+
+ System.out.println("Current working directory = " + cwd);
+
+ String os = System.getProperty("os.name").toLowerCase();
+ List<String> script = new ArrayList<String>();
+
+ if(os.contains("windows")){
+ script.add("cmd.exe");
+ script.add("/c");
+ script.add("runsanity.bat");
+ } else {
+ script.add("./runsanity.sh");
+ }
+
+ ProcessBuilder processBuilder = new ProcessBuilder();
+ processBuilder.inheritIO().command(script);
+ Process p = processBuilder.start();
+
+ copy(p.getInputStream(), System.out);
+
+ p.waitFor();
+
+ System.out.println("Test exited with exitValue = " + p.exitValue());
+
+ System.exit(p.exitValue());
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sanitytest.internal;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class Activator implements BundleActivator {
+ //10 Second initial, 1 second subsequent
+ private static final int INITIAL_DELAY = 10000;
+ private static final int SUBSEQUENT_DELAY = 1000;
+ private static final int MAX_ATTEMPTS = 120;
+
+
+ private String stateToString(int state) {
+ switch (state) {
+ case Bundle.ACTIVE:
+ return "ACTIVE";
+ case Bundle.INSTALLED:
+ return "INSTALLED";
+ case Bundle.RESOLVED:
+ return "RESOLVED";
+ case Bundle.UNINSTALLED:
+ return "UNINSTALLED";
+ case Bundle.STARTING:
+ return "STARTING";
+ default:
+ return "Not CONVERTED: state value is " + state;
+ }
+ }
+
+ public void start(final BundleContext bundleContext) throws Exception {
+ Timer monitorTimer = new Timer("monitor timer", true);
+ monitorTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ int countup = 0;
+ boolean failed = false;
+ boolean resolved = false;
+ while (!resolved) {
+ resolved = true;
+ failed = false;
+ for(Bundle bundle : bundleContext.getBundles()){
+ /*
+ * A bundle should be ACTIVE, unless it a fragment, in which case it should be RESOLVED
+ */
+ int state = bundle.getState();
+ if ((bundle.adapt(BundleRevision.class).getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
+ //fragment
+ if (state != Bundle.RESOLVED) {
+ System.out.println("------ Failed to activate/resolve fragment = " + bundle.getSymbolicName() + " state = " + stateToString(bundle.getState()));
+ failed = true;
+ if (state == Bundle.STARTING)
+ resolved = false;
+ }
+ } else {
+ if(state != Bundle.ACTIVE) {
+ System.out.println("------ Failed to activate/resolve bundle = " + bundle.getSymbolicName() + " state = " + stateToString(bundle.getState()));
+ failed = true;
+ if (state == Bundle.STARTING)
+ resolved = false;
+ }
+ }
+ }
+ if (!resolved) {
+ countup++;
+ if (countup < MAX_ATTEMPTS) {
+ System.out.println("all bundles haven't finished starting, will repeat");
+ try {
+ Thread.sleep(SUBSEQUENT_DELAY);
+ } catch (Exception e) {
+ System.out.println("Thread.sleep interuptted.");
+ break;
+ }
+ } else
+ resolved = true;
+ }
+ }
+
+ if(failed){
+ System.out.flush();
+ System.out.println("exiting with 1 as failed");
+ System.out.close();
+ Runtime.getRuntime().exit(1);
+ } else {
+ System.out.flush();
+ System.out.println("exiting with 0 as succeeded");
+ System.out.close();
+ Runtime.getRuntime().exit(0);
+ }
+ }
+ }, INITIAL_DELAY);
+ }
+
+ public void stop(BundleContext bundleContext) throws Exception {
+
+ }
+}
+++ /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.distribution.test;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.Socket;
-import java.net.URL;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Properties;
-import java.util.Set;
-import java.util.jar.Attributes;
-import java.util.jar.JarFile;
-
-import javax.management.JMX;
-import javax.management.MBeanServerConnection;
-import javax.management.Notification;
-import javax.management.NotificationListener;
-import javax.management.ObjectName;
-import javax.management.remote.JMXConnector;
-import javax.management.remote.JMXConnectorFactory;
-import javax.management.remote.JMXServiceURL;
-
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Constants;
-import org.ow2.chameleon.management.beans.BundleMXBean;
-import org.ow2.chameleon.management.beans.OSGiPlatformMXBean;
-
-/**
- * This integration test assumes a running local controller. The test does the
- * following:
- * 1) Wait for HTTP, JMX and OF ports to open
- * 2) Establishes a JMX connection and registers a bundle notification listener
- * 3) Waits till all bundles reach expected states or the timeout period elapses
- */
-public class SanityIT {
- private static final int OF_PORT = Integer.getInteger("ctrl.of.port", 6633);
- private static final int HTTP_PORT = Integer.getInteger("ctrl.http.port", 8080);
- private static final int JMX_PORT = Integer.getInteger("ctrl.jmx.port", 1088);
- private static final int RMI_PORT = Integer.getInteger("ctrl.rmi.port", 1099);
- private static final String CTRL_HOST = System.getProperty("ctrl.host", "127.0.0.1");
- private static final String CTRL_HOME = System.getProperty("ctrl.home");
- private static final long TIMEOUT_MILLIS =
- Integer.getInteger("ctrl.start.timeout", 3*60) * 1000L;
- private static final String JMX_URL =
- "service:jmx:rmi:///jndi/rmi://" + CTRL_HOST + ":" + JMX_PORT + "/jmxrmi";
-
- private static final Set<String> bundles =
- Collections.synchronizedSet(new HashSet<String>());
- private static final Set<String> fragments =
- Collections.synchronizedSet(new HashSet<String>());
-
- @BeforeClass
- public static void loadBundles() throws IOException {
- log(" ctrl.home: " + CTRL_HOME);
- log(" ctrl.host: " + CTRL_HOST);
- log("ctrl.start.timeout: " + TIMEOUT_MILLIS);
- log(" jmx.url: " + JMX_URL);
-
- Assert.assertNotNull(CTRL_HOME);
- File ctrlHome = new File(CTRL_HOME);
- Assert.assertTrue(ctrlHome.exists() && ctrlHome.isDirectory());
- File configDir = new File(ctrlHome, "configuration");
- Assert.assertTrue(configDir.exists() && configDir.isDirectory());
- File configIni = new File(configDir, "config.ini");
- Assert.assertTrue(configIni.exists());
- Properties config = new Properties();
- config.load(new FileInputStream(configIni));
- processBundles(configDir, config.getProperty("osgi.bundles"));
- processBundles(new File(ctrlHome, "plugins"));
- log("Bundles found in installation: " + bundles.size());
- log("Fragments found in installation: " + fragments.size());
- }
-
-@Test
- public void sanityTest() throws Exception {
- // wait for http, jmx & of ports to open
- long startTime = System.currentTimeMillis();
- waitForListening(OF_PORT, false, startTime);
- waitForListening(JMX_PORT, false, startTime);
- waitForListening(HTTP_PORT, false, startTime);
-
- // open jmx connection
- JMXServiceURL serviceUrl = new JMXServiceURL(JMX_URL);
- JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceUrl, null);
- final MBeanServerConnection conn = jmxConnector.getMBeanServerConnection();
-
- ObjectName fmkName = new ObjectName("org.ow2.chameleon:type=framework");
- OSGiPlatformMXBean fmkBean= JMX.newMBeanProxy(conn, fmkName,
- OSGiPlatformMXBean.class, true);
- conn.addNotificationListener(fmkName, new NotificationListener() {
-
- @Override
- public void handleNotification(Notification n, Object handback) {
- try {
- //log("Notification: source: " + n.getSource());
- ObjectName bundleName = new ObjectName(
- "org.ow2.chameleon:type=bundle,id=" + n.getUserData());
- BundleMXBean bundleBean = JMX.newMXBeanProxy(conn,
- bundleName, BundleMXBean.class, true);
- log("Bundle state change: " + bundleBean.getSymbolicName() +
- " : " + stateToString(bundleBean.getState()));
- handleBundleEvent(bundleBean);
- // if its a system bundle, notify the main thread
- if (bundleBean.getBundleId() == 0 &&
- bundleBean.getState() == Bundle.ACTIVE)
- {
- synchronized(SanityIT.this) {
- SanityIT.this.notify();
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }, null, null);
-
- if (checkAllBundles(conn) > 0) {
- log("Waiting for system bundle to start... (times out in: " + TIMEOUT_MILLIS + "ms)");
- long timeElapsed = System.currentTimeMillis() - startTime;
- synchronized(this) {
- this.wait(TIMEOUT_MILLIS - timeElapsed);
- }
- log("System bundle started. Revalidating bundle states for all bundles...");
-
- // Sometimes, the system bundle starts earlier than other bundles(?). The
- // following loop will cover that case.
- do {
- Thread.sleep(2000); // 2s seems appropriate given the default timeout
- if (checkAllBundles(conn) == 0) {
- break;
- }
- } while(System.currentTimeMillis() - startTime < TIMEOUT_MILLIS);
- }
- try {
- jmxConnector.close();
- } catch (Exception ignore) {
- // dont want the tests to fail in case we can't close jmx connection
- ignore.printStackTrace();
- }
- if (bundles.size() + fragments.size() == 0) {
- log("All bundles have reached expected state");
- } else {
- log("The following bundles did not reach expected state: ");
- for (String s : bundles) log("Bundle: " + s);
- for (String s : fragments) log("Fragment: " + s);
-
- }
- Assert.assertTrue(bundles.size() == 0 && fragments.size() == 0);
- }
-
- private static int checkAllBundles(MBeanServerConnection conn) throws Exception {
- ObjectName allBundlesName = new ObjectName("org.ow2.chameleon:*");
- Set<ObjectName> bundleNames = conn.queryNames(allBundlesName, null);
- for (ObjectName bundleName : bundleNames) {
- if ("bundle".equals(bundleName.getKeyProperty("type"))) {
- BundleMXBean bundleBean = JMX.newMBeanProxy(conn, bundleName,
- BundleMXBean.class, true);
- handleBundleEvent(bundleBean);
- }
- }
- int remaining = bundles.size() + fragments.size();
- if (remaining > 0) {
- log("Bundles not in expected states: " + remaining + " Waiting...");
- }
- return remaining;
- }
-
- private synchronized static void handleBundleEvent(BundleMXBean bundleBean) {
- String name = bundleBean.getSymbolicName();
- int state = bundleBean.getState();
- // BUG in BundleMXBeanImpl - can't get bundle headers :(
- // String fragHost = bundleBean.getBundleHeaders().get(Constants.FRAGMENT_HOST);
- if (bundles.contains(name) && (state == Bundle.RESOLVED || state == Bundle.ACTIVE)) {
- bundles.remove(name);
- } else if (fragments.contains(name) && state == Bundle.RESOLVED) {
- fragments.remove(name);
- }
- //log("Bundles to be started: " + bundles.size());
- }
-
-
- // reference\:file\:../lib/org.apache.felix.fileinstall-3.1.6.jar@1:start,\
- private static void processBundles(File dir, String property) {
- Assert.assertTrue(property == null || property.length() > 0);
- for(String s : property.split(",")) {
- int idx = s.indexOf("@");
- s = s.substring(15, (idx == -1 ? s.length() : idx));
- if (!s.endsWith(".jar")) s = s + ".jar";
- processJar(new File(dir, s));
- }
- }
-
- public static void processBundles(File dir) throws IOException {
- if (!dir.exists()) throw new FileNotFoundException("Cannot find dir:" + dir);
- dir.listFiles(new FileFilter() {
-
- @Override
- public boolean accept(File file) {
- //p("---- " + file);
- if (file.getName().endsWith(".jar")) {
- return processJar(file);
- }
- return false;
- }
- });
- }
-
- public static boolean processJar(File file) {
- try {
- //log("----" + file);
- JarFile jar = new JarFile(file, false);
- Attributes jarAttributes = jar.getManifest().getMainAttributes();
- String bundleName = jarAttributes.getValue(Constants.BUNDLE_SYMBOLICNAME);
- String fragHost = jarAttributes.getValue(Constants.FRAGMENT_HOST);
- if (bundleName == null) {
- log("Found a non bundle file:" + file);
- return false;
- } else {
- int idx = bundleName.indexOf(';');
- if (idx > -1) {
- bundleName = bundleName.substring(0, idx);
- }
- }
-
- if (fragHost == null) {
- if (!bundles.add(bundleName)) {
- throw new IllegalStateException(
- "Found duplicate bundles with same symbolic name: "
- + bundleName);
- }
- } else {
- // fragments attaching to framework can't be detected
- if (fragHost.contains("extension:=\"framework\"")) return false;
- if (!fragments.add(bundleName)) {
- throw new IllegalStateException(
- "Found duplicate fragments with same symbolic name: "
- + bundleName);
- }
- }
- } catch (IOException e) {
- throw new IllegalStateException("Error processing jar: " + file , e);
- }
- return true;
- }
-
- public static long waitForListening(int port, boolean isHTTP, long beginTime)
- throws InterruptedException {
- long timeElapsedMillis = System.currentTimeMillis() - beginTime;
- long sleepTimeMillis = 500L; // 0.5 secs
-
- while (timeElapsedMillis < TIMEOUT_MILLIS) {
- long timeRemaining = TIMEOUT_MILLIS - timeElapsedMillis;
- sleepTimeMillis *= 2; // exponential backoff
- long toSleep = (sleepTimeMillis > timeRemaining)
- ? timeRemaining : sleepTimeMillis;
- Thread.sleep(toSleep);
- timeElapsedMillis = System.currentTimeMillis() - beginTime;
- if (isHTTP ? connectHTTP(port) : connectTCP(port)) {
- log("Port is open: " + port);
- return timeElapsedMillis;
- }
- }
- throw new IllegalStateException("Timeout waiting for port: " + port);
- }
-
- private static void log(String msg) {
- System.out.format("[SanityIT] [%s] %s %s", new Date().toString(), msg,
- System.lineSeparator());
- }
-
- public static boolean connectTCP(int port) {
- String host = CTRL_HOST.length() == 0 ? null : CTRL_HOST;
- try {
- Socket sock = new Socket(host, port);
- sock.getPort();
- try {
- sock.close();
- } catch (IOException ignore) {
- // Can't close socket. Ingore and let downstream validate health
- }
- return true;
- } catch (IOException ioe) {
- return false;
- }
- }
-
- public static boolean connectHTTP(int port) {
- String host = CTRL_HOST.length() == 0 ? "localhost" : CTRL_HOST;
- try {
- URL url = new URL("http", host, port, "/");
- HttpURLConnection con;
- con = (HttpURLConnection) url.openConnection();
- return (con.getResponseCode() > 0);
- } catch (IOException e) {
- return false;
- }
- }
-
- private String stateToString(int state) {
- switch (state) {
- case Bundle.ACTIVE: return "ACTIVE";
- case Bundle.INSTALLED: return "INSTALLED";
- case Bundle.RESOLVED: return "RESOLVED";
- case Bundle.UNINSTALLED: return "UNINSTALLED";
- case Bundle.STARTING: return "STARTING";
- case Bundle.STOPPING: return "STOPPING";
- default: return "UNKNOWN: " + state;
- }
- }
-
-
-
-
-}
import org.opendaylight.controller.sal.reader.NodeTableStatistics;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter64;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForAllFlowsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForGivenMatchInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForGivenMatchOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowStatisticsFromFlowTableInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowStatisticsFromFlowTableOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowStatisticsInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowStatisticsOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowStatisticsOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllNodeConnectorStatisticsInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllNodeConnectorStatisticsOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllNodeConnectorStatisticsOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsOutputBuilder;
return builder.build();
}
+ @Override
+ public Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput>> getAggregateFlowStatisticsFromFlowTableForAllFlows(
+ GetAggregateFlowStatisticsFromFlowTableForAllFlowsInput input) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForGivenMatchOutput>> getAggregateFlowStatisticsFromFlowTableForGivenMatch(
+ GetAggregateFlowStatisticsFromFlowTableForGivenMatchInput input) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Future<RpcResult<GetAllFlowStatisticsFromFlowTableOutput>> getAllFlowStatisticsFromFlowTable(
+ GetAllFlowStatisticsFromFlowTableInput input) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Future<RpcResult<GetAllFlowsStatisticsFromAllFlowTablesOutput>> getAllFlowsStatisticsFromAllFlowTables(
+ GetAllFlowsStatisticsFromAllFlowTablesInput input) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Future<RpcResult<GetFlowStatisticsFromFlowTableOutput>> getFlowStatisticsFromFlowTable(
+ GetFlowStatisticsFromFlowTableInput input) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
}
revision "2013-10-26" {
description "Initial revision of macth types";
}
-
+
grouping "mac-address-filter" {
leaf address {
mandatory true;
container vlan-id {
description "VLAN id.";
presence "Match field is active and set";
-
+
+ leaf vlan-id-present {
+ type boolean;
+ }
+
leaf vlan-id {
- mandatory true;
type l2t:vlan-id;
}
- leaf mask {
- type binary;
- }
}
leaf vlan-pcp {
description "VLAN priority.";
}
}
}
-
+
+ case strip-vlan-action-case {
+ container strip-vlan-action {
+ }
+ }
case sw-path-action-case {
container sw-path-action {
}
}
}
-}
\ No newline at end of file
+}
description "Initial revision of flow service";
}
+ typedef output-port-values {
+ type enumeration {
+ enum MAX {
+ value 1;
+ }
+ enum IN_PORT {
+ value 2;
+ }
+ enum TABLE {
+ value 3;
+ }
+ enum NORMAL {
+ value 4;
+ }
+ enum FLOOD {
+ value 5;
+ }
+ enum ALL {
+ value 6;
+ }
+ enum CONTROLLER {
+ value 7;
+ }
+ enum LOCAL {
+ value 8;
+ }
+ enum ANY {
+ value 9;
+ }
+ enum NONE {
+ value 10;
+ }
+
+ }
+ }
grouping instruction-list {
list instruction {
key "order";
uses match:match;
}
}
-}
\ No newline at end of file
+}
description "Initial revision of table service";
}
+ typedef table-id {
+ type uint8;
+ }
+
typedef table-ref {
type instance-identifier;
}
revision "2013-08-19" {
description "Flow Capable Node extensions to the Inventory model";
}
-
+
+ identity feature-capability {
+ }
+
+ identity flow-feature-capability-flow-stats {
+ description "Flow statistics";
+ base feature-capability;
+ }
+
+ identity flow-feature-capability-table-stats {
+ description "Table statistics";
+ base feature-capability;
+ }
+
+ identity flow-feature-capability-port-stats {
+ description "Port statistics";
+ base feature-capability;
+ }
+
+ identity flow-feature-capability-stp {
+ description "802.1d spanning tree";
+ base feature-capability;
+ }
+
+ identity flow-feature-capability-reserved {
+ description "Reserved, must be zero";
+ base feature-capability;
+ }
+
+ identity flow-feature-capability-ip-reasm {
+ description "Can reassemble IP fragments";
+ base feature-capability;
+ }
+
+ identity flow-feature-capability-queue-stats {
+ description "Queue statistics";
+ base feature-capability;
+ }
+
+ identity flow-feature-capability-arp-match-ip {
+ description "Match IP addresses in ARP pkts";
+ base feature-capability;
+ }
+
+ identity flow-feature-capability-group-stats {
+ description "Group statistics";
+ base feature-capability;
+ }
+
+ identity flow-feature-capability-port-blocked {
+ description "Switch will block looping ports";
+ base feature-capability;
+ }
+
grouping feature {
leaf support-state {
type inv:support-type;
uses meter:meter;
}
}
-
+
+
grouping flow-node {
leaf manufacturer {
}
}
}
+
+ container switch-features {
+
+ leaf max_buffers {
+ type uint32;
+ }
+
+ leaf max_tables {
+ type uint8;
+ }
+
+ leaf-list capabilities {
+ type identityref {
+ base feature-capability;
+ }
+ }
+
+ }
}
grouping flow-node-connector {
ext:augment-identifier "flow-capable-node-connector-update-fields";
uses flow-node-connector;
}
-
+
augment "/inv:node-connector-updated" {
ext:augment-identifier "flow-capable-node-connector-updated";
uses flow-node-connector;
}
+
}
\ No newline at end of file
prefix flowstat;
import yang-ext {prefix ext; revision-date "2013-07-09";}
+ import ietf-yang-types {prefix yang; revision-date "2010-09-24";}
import opendaylight-inventory {prefix inv;revision-date "2013-08-19";}
import opendaylight-flow-types {prefix flow-types;revision-date "2013-10-26";}
- import sal-flow {prefix flow;}
import opendaylight-statistics-types {prefix stat-types;revision-date "2013-09-25";}
+ import opendaylight-table-types {prefix table-types;revision-date "2013-10-26";}
+ import flow-node-inventory {prefix flow-node;revision-date "2013-08-19";}
+ import flow-capable-transaction {prefix tr;}
+ import sal-flow {prefix flow;}
+
revision "2013-08-19" {
- description "Initial revision of flow service";
+ description "Initial revision of flow statistics service";
+ }
+
+ //Augment flow statistics data to the flow-capable-node->table->flow
+ augment "/inv:nodes/inv:node/flow-node:table/flow-node:flow" {
+ ext:augment-identifier "flow-statistics-data";
+ uses flow-statistics;
+ }
+
+ grouping flow-statistics {
+ container flow-statistics {
+ //config "false";
+ uses flow-types:flow;
+ uses stat-types:generic-statistics;
+ }
+ }
+
+ typedef flow-id {
+ description "flow id";
+ type yang:counter32;
+ }
+
+ grouping flow-and-statistics-map-list {
+ description "List of flow and statistics map";
+ list flow-and-statistics-map-list {
+ key "flow-id";
+ leaf flow-id {
+ type flow-id;
+ }
+ uses flow-and-statistics-map;
+ }
+ }
+
+ grouping flow-and-statistics-map{
+ description "Mapping between flow and its statistics";
+ uses flow-types:flow;
+ uses stat-types:generic-statistics;
+ }
+
+ // RPC calls to fetch flow statistics
+ rpc get-all-flows-statistics-from-all-flow-tables {
+ description "Fetch statistics of all the flow present in all the flow tables of the switch";
+ input {
+ uses inv:node-context-ref;
+ }
+ output {
+ uses flow-and-statistics-map-list;
+ uses tr:transaction-aware;
+ }
+
}
- rpc get-node-connector-statistics {
+ rpc get-all-flow-statistics-from-flow-table {
+ description "Fetch statistics of all the flow present in the specific flow table of the switch";
input {
uses inv:node-context-ref;
- leaf node-connector {
- type inv:node-connector-ref;
+ leaf table-id {
+ type table-types:table-id;
}
}
output {
- uses stat-types:node-connector-statistics;
+ uses flow-and-statistics-map-list;
+ uses tr:transaction-aware;
}
}
- rpc get-flow-statistics {
+ rpc get-flow-statistics-from-flow-table {
+ description "Fetch statistics of the specific flow present in the specific flow table of the switch";
+ input {
+ uses inv:node-context-ref;
+ uses flow-types:flow;
+ }
+ output {
+ uses flow-and-statistics-map-list;
+ uses tr:transaction-aware;
+ }
+ }
+
+ notification flows-statistics-update {
+ description "Flows statistics sent by switch";
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses flow-and-statistics-map-list;
+ uses tr:transaction-aware;
+ }
+
+ //Models for aggregate flow statistics collection
+ augment "/inv:nodes/inv:node/flow-node:table" {
+ ext:augment-identifier "aggregate-flow-statistics-data";
+ uses aggregate-flow-statistics;
+ }
+
+ grouping aggregate-flow-statistics {
+ container aggregate-flow-statistics {
+ //config "false";
+ uses stat-types:aggregate-flow-statistics;
+ }
+ }
+
+ // RPC calls to fetch flow statistics
+ rpc get-aggregate-flow-statistics-from-flow-table-for-all-flows {
+ description "Fetch aggregate statistics for all the flows present in the specific flow table of the switch";
+ input {
+ uses inv:node-context-ref;
+ leaf table-id {
+ type table-types:table-id;
+ }
+ }
+ output {
+ uses stat-types:aggregate-flow-statistics;
+ uses tr:transaction-aware;
+ }
+ }
+ rpc get-aggregate-flow-statistics-from-flow-table-for-given-match {
+ description "Fetch aggregate statistics for all the flow matches to the given match from the given table of the switch";
+ input {
+ uses inv:node-context-ref;
+ uses flow-types:flow;
+ }
+ output {
+ uses stat-types:aggregate-flow-statistics;
+ uses tr:transaction-aware;
+ }
+ }
+
+ notification aggregate-flow-statistics-update {
+ description "Aggregate flow statistics for a table, sent by switch";
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses stat-types:aggregate-flow-statistics;
+ uses tr:transaction-aware;
+ }
+
+ //Keeping flow statistics RPC call for backward compatibility for sal-compatibility layer --START
+ rpc get-flow-statistics {
input {
uses inv:node-context-ref;
uses flow-types:flow;
}
}
+ notification flow-statistics-updated {
+ uses flow-types:flow-statistics;
+ }
+
+ //Keeping flow statistics RPC call for backward compatibility for sal-compatibility layer --END
+
+ //RPC call to fetch node connector statistics
+ rpc get-node-connector-statistics {
+ input {
+ uses inv:node-context-ref;
+ leaf node-connector {
+ type inv:node-connector-ref;
+ }
+ }
+ output {
+ uses stat-types:node-connector-statistics;
+ }
+ }
+
rpc get-all-node-connector-statistics {
input {
uses inv:node-context-ref;
}
}
- notification flow-statistics-updated {
- uses flow-types:flow-statistics;
- }
-
rpc get-flow-table-statistics {
input {
uses inv:node-context-ref;
notification node-connector-statistics-updated {
uses stat-types:node-connector-statistics;
}
-
-
}
--- /dev/null
+module opendaylight-flow-table-statistics {
+ namespace "urn:opendaylight:flow:table:statistics";
+ prefix flowtablestat;
+
+ import flow-capable-transaction {prefix tr;}
+ import yang-ext {prefix ext; revision-date "2013-07-09";}
+ import ietf-yang-types {prefix yang; revision-date "2010-09-24";}
+ import opendaylight-inventory {prefix inv;revision-date "2013-08-19";}
+ import flow-node-inventory {prefix flow-node;revision-date "2013-08-19";}
+ import opendaylight-table-types {prefix table-types;revision-date "2013-10-26";}
+ import opendaylight-statistics-types {prefix stat-types;revision-date "2013-09-25";}
+
+
+ contact
+ "Anilkumar Vishnoi
+ Email: avishnoi@in.ibm.com";
+
+ revision "2013-12-15" {
+ description "Initial revision of flow table statistics model";
+ }
+
+ //Augment flow table statistics data to the table
+ augment "/inv:nodes/inv:node/flow-node:table" {
+ ext:augment-identifier "flow-table-statistics-data";
+ uses flow-table-statistics;
+ }
+
+ grouping flow-table-statistics {
+ container flow-table-statistics {
+ //config "false";
+ uses stat-types:generic-table-statistics;
+ }
+ }
+
+ //RPC calls to fetch flow table statistics
+ grouping flow-table-and-statistics-map {
+ list flow-table-and-statistics-map {
+ key "table-id";
+ leaf table-id {
+ type table-types:table-id;
+ }
+ uses stat-types:generic-table-statistics;
+ }
+ }
+
+ rpc get-flow-tables-statistics {
+ description "Fetch statistics of all the flow tables present on the tarnet node";
+ input {
+ uses inv:node-context-ref;
+ }
+ output {
+ uses flow-table-and-statistics-map;
+ uses tr:transaction-aware;
+ }
+ }
+
+ //Notification to receive table statistics update
+
+ notification flow-table-statistics-update {
+ description "Receive flow table statistics update";
+
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses flow-table-and-statistics-map;
+ uses tr:transaction-aware;
+ }
+}
--- /dev/null
+module opendaylight-port-statistics {
+ namespace "urn:opendaylight:port:statistics";
+ prefix portstat;
+
+ import flow-capable-transaction {prefix tr;}
+ import yang-ext {prefix ext; revision-date "2013-07-09";}
+ import ietf-yang-types {prefix yang; revision-date "2010-09-24";}
+ import opendaylight-inventory {prefix inv;revision-date "2013-08-19";}
+ import opendaylight-statistics-types {prefix stat-types;revision-date "2013-09-25";}
+
+ contact
+ "Anilkumar Vishnoi
+ Email: avishnoi@in.ibm.com";
+
+ revision "2013-12-14" {
+ description "Initial revision of port statistics model";
+ }
+
+ //Augment port statistics data to the flow-capable-node-connector
+ augment "/inv:nodes/inv:node/inv:node-connector" {
+ ext:augment-identifier "flow-capable-node-connector-statistics-data";
+ uses flow-capable-node-connector-statistics;
+ }
+
+ grouping flow-capable-node-connector-statistics {
+ container flow-capable-node-connector-statistics {
+ //config "false";
+ uses stat-types:node-connector-statistics;
+ }
+ }
+
+ // RPC calls
+ rpc get-all-ports-statistics {
+ description "Get statistics for all the ports from the node";
+ input {
+ uses inv:node-context-ref;
+ }
+ output {
+ uses stat-types:node-connector-statistics;
+ uses tr:transaction-aware;
+ }
+ }
+
+ rpc get-port-statistics {
+ description "Get statistics for given port from the node";
+ input {
+ uses inv:node-context-ref;
+ leaf node-connector-id {
+ type inv:node-connector-id;
+ }
+ }
+ output {
+ uses stat-types:node-connector-statistics;
+ uses tr:transaction-aware;
+ }
+ }
+
+ //Notification for port statistics update
+ grouping node-connector-statistics-and-port-number-map {
+ description "List of flow and statistics map";
+ list node-connector-statistics-and-port-number-map {
+ key "node-connector-id";
+ leaf node-connector-id {
+ type inv:node-connector-id;
+ }
+ uses stat-types:node-connector-statistics;
+ }
+ }
+
+ notification port-statistics-update {
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses node-connector-statistics-and-port-number-map;
+ uses tr:transaction-aware;
+ }
+}
}
}
}
+
+ grouping generic-statistics {
+ description "Generic grouping for statistics";
+ leaf packet-count {
+ type yang:counter64;
+ }
+
+ leaf byte-count {
+ type yang:counter64;
+ }
+
+ container duration {
+ leaf second {
+ type yang:counter64;
+ }
+ leaf nanosecond {
+ type yang:counter64;
+ }
+ }
+ }
+
+ grouping generic-table-statistics {
+ description "Generic grouping holding generic statistics related to switch table";
+ leaf active-flows {
+ type yang:counter32;
+ }
+ leaf packets-looked-up {
+ type yang:counter64;
+ }
+ leaf packets-matched {
+ type yang:counter64;
+ }
+ }
+
+ grouping aggregate-flow-statistics {
+ description "Aggregate flow statistics";
+ leaf packet-count {
+ type yang:counter64;
+ }
+
+ leaf byte-count {
+ type yang:counter64;
+ }
+ leaf flow-count {
+ type yang:counter32;
+ }
+ }
+
}
\ No newline at end of file
<sonar.host.url>https://sonar.opendaylight.org/</sonar.host.url>
<sonar.branch>${user.name}-private-view</sonar.branch>
<sonar.language>java</sonar.language>
+ <exam.version>3.0.0</exam.version>
</properties>
<pluginRepositories>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${project.build.directory}/generated-sources/config</source>
+ <source>${project.build.directory}/generated-sources/sal</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
--- /dev/null
+package org.opendaylight.controller.sal.binding.api;
+
+public interface RpcAvailabilityListener {
+
+}
<artifactId>ietf-inet-types</artifactId>
<version>2010.09.24.2-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-topology-l3-unicast-igp</artifactId>
+ <version>2013.10.21.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-flow-base</artifactId>
import org.opendaylight.controller.config.yang.md.sal.binding.statistics.DataBrokerRuntimeMXBeanImpl;\r
import org.opendaylight.controller.md.sal.common.impl.routing.AbstractDataReadRouter;\r
import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl;\r
-import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentDataServiceConnector;\r
+import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector;\r
import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService;\r
import org.opendaylight.controller.sal.core.api.Broker;\r
import org.opendaylight.controller.sal.core.api.data.DataProviderService;\r
BindingIndependentMappingService mappingService = getMappingServiceDependency();\r
\r
if (domBroker != null && mappingService != null) {\r
- BindingIndependentDataServiceConnector runtimeMapping = new BindingIndependentDataServiceConnector();\r
+ BindingIndependentConnector runtimeMapping = new BindingIndependentConnector();\r
runtimeMapping.setMappingService(mappingService);\r
runtimeMapping.setBaDataService(dataBindingBroker);\r
domBroker.registerProvider(runtimeMapping, getBundleContext());\r
import javassist.ClassPool;
+import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
import org.opendaylight.controller.sal.binding.dom.serializer.impl.RuntimeGeneratedMappingServiceImpl;
import org.osgi.framework.BundleContext;
@Override
public java.lang.AutoCloseable createInstance() {
RuntimeGeneratedMappingServiceImpl service = new RuntimeGeneratedMappingServiceImpl();
- ClassPool pool = new ClassPool(); // Should be default singleton
- service.setPool(pool);
+ service.setPool(SingletonHolder.CLASS_POOL);
service.start(getBundleContext());
return service;
}
if (field == null) throw new UnsupportedOperationException(
"Unable to set routing table. Table field does not exists");
field.set(target,routingTable);
-
}
}
--- /dev/null
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.spi.RpcRouter;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.*;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.HashMap;
+
+import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.RpcImplementation;
+import org.opendaylight.controller.md.sal.common.api.routing.MutableRoutingTable;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class RpcRouterCodegenInstance<T extends RpcService> implements //
+ RpcRouter<T>, RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RpcRouterCodegenInstance.class);
+
+ private T defaultService;
+
+ private final Class<T> serviceType;
+
+ private final T invocationProxy;
+
+ private final Set<Class<? extends BaseIdentity>> contexts;
+
+ private final ListenerRegistry<RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> listeners;
+
+ private final Map<Class<? extends BaseIdentity>, RpcRoutingTableImpl<? extends BaseIdentity, T>> routingTables;
+
+ public RpcRouterCodegenInstance(Class<T> type, T routerImpl, Set<Class<? extends BaseIdentity>> contexts,
+ Set<Class<? extends DataContainer>> inputs) {
+ this.listeners = ListenerRegistry.create();
+ this.serviceType = type;
+ this.invocationProxy = routerImpl;
+ this.contexts = ImmutableSet.copyOf(contexts);
+ Map<Class<? extends BaseIdentity>, RpcRoutingTableImpl<? extends BaseIdentity, T>> mutableRoutingTables = new HashMap<>();
+ for (Class<? extends BaseIdentity> ctx : contexts) {
+ RpcRoutingTableImpl<? extends BaseIdentity, T> table = new RpcRoutingTableImpl<>(ctx);
+ Map invokerView = table.getRoutes();
+ setRoutingTable((RpcService) invocationProxy, ctx, invokerView);
+ mutableRoutingTables.put(ctx, table);
+ table.registerRouteChangeListener(this);
+ }
+ this.routingTables = ImmutableMap.copyOf(mutableRoutingTables);
+ }
+
+ @Override
+ public Class<T> getServiceType() {
+ return serviceType;
+ }
+
+ @Override
+ public T getInvocationProxy() {
+ return invocationProxy;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <C extends BaseIdentity> RpcRoutingTable<C, T> getRoutingTable(Class<C> routeContext) {
+ return (RpcRoutingTable<C, T>) routingTables.get(routeContext);
+ }
+
+ @Override
+ public T getDefaultService() {
+ return defaultService;
+ }
+
+ @Override
+ public Set<Class<? extends BaseIdentity>> getContexts() {
+ return contexts;
+ }
+
+ @Override
+ public <L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+ L listener) {
+ return listeners.registerWithType(listener);
+ }
+
+ @Override
+ public void onRouteChange(RouteChange<Class<? extends BaseIdentity>, InstanceIdentifier<?>> change) {
+ for (ListenerRegistration<RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> listener : listeners) {
+ try {
+ listener.getInstance().onRouteChange(change);
+ } catch (Exception e) {
+ LOG.error("Error occured during invoker listener {}", listener.getInstance(), e);
+ }
+ }
+ }
+
+ @Override
+ public T getService(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
+ return routingTables.get(context).getRoute(path);
+ }
+
+ @Override
+ public RoutedRpcRegistration<T> addRoutedRpcImplementation(T service) {
+ return new RoutedRpcRegistrationImpl(service);
+ }
+
+ @Override
+ public RpcRegistration<T> registerDefaultService(T service) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private class RoutedRpcRegistrationImpl extends AbstractObjectRegistration<T> implements RoutedRpcRegistration<T> {
+
+ public RoutedRpcRegistrationImpl(T instance) {
+ super(instance);
+ }
+
+ @Override
+ public Class<T> getServiceType() {
+ return serviceType;
+ }
+
+ @Override
+ public void registerPath(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
+ routingTables.get(context).updateRoute(path, getInstance());
+ }
+
+ @Override
+ public void unregisterPath(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
+ routingTables.get(context).removeRoute(path, getInstance());
+
+ }
+
+ @Override
+ public void registerInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<?> instance) {
+ registerPath(context, instance);
+ }
+
+ @Override
+ public void unregisterInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<?> instance) {
+ unregisterPath(context, instance);
+ }
+
+ @Override
+ protected void removeRegistration() {
+
+ }
+ }
+}
+++ /dev/null
-package org.opendaylight.controller.sal.binding.codegen.impl
-
-import org.opendaylight.yangtools.yang.binding.RpcService
-import org.opendaylight.controller.sal.binding.spi.RpcRouter
-import org.opendaylight.yangtools.yang.binding.BaseIdentity
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.*
-import java.util.Set
-import java.util.HashMap
-import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable
-import org.opendaylight.yangtools.yang.binding.DataContainer
-import org.opendaylight.yangtools.yang.binding.RpcImplementation
-
-class RpcRouterCodegenInstance<T extends RpcService> implements RpcRouter<T> {
-
- @Property
- val T invocationProxy
-
- @Property
- val RpcImplementation invokerDelegate;
-
- @Property
- val Class<T> serviceType
-
- @Property
- val Set<Class<? extends BaseIdentity>> contexts
-
- @Property
- val Set<Class<? extends DataContainer>> supportedInputs;
-
- val routingTables = new HashMap<Class<? extends BaseIdentity>, RpcRoutingTableImpl<? extends BaseIdentity, ? extends RpcService>>;
-
- @Property
- var T defaultService
-
- new(Class<T> type, T routerImpl, Set<Class<? extends BaseIdentity>> contexts,
- Set<Class<? extends DataContainer>> inputs) {
- _serviceType = type
- _invocationProxy = routerImpl
- _invokerDelegate = routerImpl as RpcImplementation
- _contexts = contexts
- _supportedInputs = inputs;
-
- for (ctx : contexts) {
- val table = XtendHelper.createRoutingTable(ctx)
- invocationProxy.setRoutingTable(ctx, table.routes);
- routingTables.put(ctx, table);
- }
- }
-
- override <C extends BaseIdentity> getRoutingTable(Class<C> table) {
- routingTables.get(table) as RpcRoutingTable<C,T>
- }
-
- override getService(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
- val table = getRoutingTable(context);
- return table.getRoute(path);
- }
-
- override <T extends DataContainer> invoke(Class<T> type, T input) {
- return invokerDelegate.invoke(type, input);
- }
-
-}
--- /dev/null
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Mutable;
+
+class RpcRoutingTableImpl<C extends BaseIdentity, S extends RpcService> //
+implements //
+ Mutable, //
+ RpcRoutingTable<C, S>, //
+ RouteChangePublisher<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
+
+ private final Class<C> identifier;
+ private final ConcurrentMap<InstanceIdentifier<?>, S> routes;
+ private final Map<InstanceIdentifier<?>, S> unmodifiableRoutes;
+
+ private RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> listener;
+ private S defaultRoute;
+
+ public RpcRoutingTableImpl(Class<C> identifier) {
+ super();
+ this.identifier = identifier;
+ this.routes = new ConcurrentHashMap<>();
+ this.unmodifiableRoutes = Collections.unmodifiableMap(routes);
+ }
+
+ @Override
+ public void setDefaultRoute(S target) {
+ defaultRoute = target;
+ }
+
+ @Override
+ public S getDefaultRoute() {
+ return defaultRoute;
+ }
+
+ @Override
+ public <L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+ L listener) {
+ return (ListenerRegistration<L>) new SingletonListenerRegistration<L>(listener);
+ }
+
+ @Override
+ public Class<C> getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void updateRoute(InstanceIdentifier<?> path, S service) {
+ S previous = this.routes.put(path, service);
+ @SuppressWarnings("rawtypes")
+ RouteChangeListener listenerCapture = listener;
+ if (previous == null && listenerCapture != null) {
+ listenerCapture.onRouteChange(RoutingUtils.announcementChange(identifier, path));
+ }
+ }
+
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void removeRoute(InstanceIdentifier<?> path) {
+ S previous = this.routes.remove(path);
+ @SuppressWarnings("rawtypes")
+ RouteChangeListener listenerCapture = listener;
+ if (previous != null && listenerCapture != null) {
+ listenerCapture.onRouteChange(RoutingUtils.removalChange(identifier, path));
+ }
+ }
+
+ public void removeRoute(InstanceIdentifier<?> path, S service) {
+ @SuppressWarnings("rawtypes")
+ RouteChangeListener listenerCapture = listener;
+ if (routes.remove(path, service) && listenerCapture != null) {
+ listenerCapture.onRouteChange(RoutingUtils.removalChange(identifier, path));
+ }
+ }
+
+ @Override
+ public S getRoute(InstanceIdentifier<?> nodeInstance) {
+ S route = routes.get(nodeInstance);
+ if (route != null) {
+ return route;
+ }
+ return getDefaultRoute();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, S> getRoutes() {
+ return unmodifiableRoutes;
+ }
+
+ protected void removeAllReferences(S service) {
+
+ }
+
+ private class SingletonListenerRegistration<L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> extends
+ AbstractObjectRegistration<L>
+ implements ListenerRegistration<L> {
+
+ public SingletonListenerRegistration(L instance) {
+ super(instance);
+ listener = instance;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ listener = null;
+ }
+ }
+}
\ No newline at end of file
+++ /dev/null
-package org.opendaylight.controller.sal.binding.codegen.impl
-
-import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable
-import org.opendaylight.yangtools.yang.binding.BaseIdentity
-import org.opendaylight.yangtools.yang.binding.RpcService
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import java.util.Map
-import org.opendaylight.yangtools.yang.binding.DataObject
-import java.util.HashMap
-
-class RpcRoutingTableImpl<C extends BaseIdentity,S extends RpcService> implements RpcRoutingTable<C,S>{
-
- @Property
- val Class<C> identifier;
-
- @Property
- var S defaultRoute;
-
- @Property
- val Map<InstanceIdentifier<? extends DataObject>,S> routes;
-
- new(Class<C> ident, Map<InstanceIdentifier<? extends DataObject>,S> route) {
- _identifier = ident
- _routes = route
- }
-
- new(Class<C> ident) {
- _identifier = ident
- _routes = new HashMap
- }
-
-
- override getRoute(InstanceIdentifier<? extends Object> nodeInstance) {
- val ret = routes.get(nodeInstance);
- if(ret !== null) {
- return ret;
- }
- return defaultRoute;
- }
-
- override removeRoute(InstanceIdentifier<? extends Object> path) {
- routes.remove(path);
- }
-
- @SuppressWarnings("rawtypes")
- override updateRoute(InstanceIdentifier<? extends Object> path, S service) {
- routes.put(path as InstanceIdentifier<? extends DataObject>,service);
- }
-}
\ No newline at end of file
import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
import java.util.HashSet
-import static org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils.*
+import static org.opendaylight.yangtools.concepts.util.ClassLoaderUtils.*
import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
import java.util.Set
import org.opendaylight.yangtools.yang.binding.DataContainer
import org.opendaylight.yangtools.yang.binding.RpcImplementation
import org.opendaylight.controller.sal.binding.codegen.util.JavassistUtils
+import org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils
+import javassist.LoaderClassPath
class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
val extension JavassistUtils utils;
val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
- public new(ClassPool pool) {
+
+ new(ClassPool pool) {
classPool = pool;
utils = new JavassistUtils(pool);
invokerClasses = new WeakHashMap();
BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
+ pool.appendClassPath(new LoaderClassPath(RpcService.classLoader));
}
override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
- val supertype = iface.asCtClass
- val targetCls = createClass(iface.directProxyName, supertype) [
- field(DELEGATE_FIELD, iface);
- implementMethodsFrom(supertype) [
- body = '''
- {
- if(«DELEGATE_FIELD» == null) {
+ val T instance = withClassLoaderAndLock(iface.classLoader,lock) [|
+ val proxyName = iface.directProxyName;
+ val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName)
+ if(potentialClass != null) {
+ return potentialClass.newInstance as T;
+ }
+ val supertype = iface.asCtClass
+ val createdCls = createClass(iface.directProxyName, supertype) [
+ field(DELEGATE_FIELD, iface);
+ implementsType(RpcImplementation.asCtClass)
+ implementMethodsFrom(supertype) [
+ body = '''
+ {
+ if(«DELEGATE_FIELD» == null) {
+ throw new java.lang.IllegalStateException("No provider is processing supplied message");
+ }
+ return ($r) «DELEGATE_FIELD».«it.name»($$);
+ }
+ '''
+ ]
+ implementMethodsFrom(RpcImplementation.asCtClass) [
+ body = '''
+ {
throw new java.lang.IllegalStateException("No provider is processing supplied message");
+ return ($r) null;
}
- return ($r) «DELEGATE_FIELD».«it.name»($$);
- }
- '''
+ '''
+ ]
]
+ return createdCls.toClass(iface.classLoader).newInstance as T
]
- return targetCls.toClass(iface.classLoader).newInstance as T
+ return instance;
}
override <T extends RpcService> getRouterFor(Class<T> iface) {
- val instance = <RpcRouterCodegenInstance<T>>withClassLoaderAndLock(iface.classLoader,lock) [ |
+ val metadata = withClassLoader(iface.classLoader) [|
+ val supertype = iface.asCtClass
+ return supertype.rpcMetadata;
+ ]
+
+ val instance = <T>withClassLoaderAndLock(iface.classLoader,lock) [ |
val supertype = iface.asCtClass
- val metadata = supertype.rpcMetadata;
+ val routerName = iface.routerName;
+ val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName)
+ if(potentialClass != null) {
+ return potentialClass.newInstance as T;
+ }
+
val targetCls = createClass(iface.routerName, supertype) [
- addInterface(RpcImplementation.asCtClass)
+
field(DELEGATE_FIELD, iface)
//field(REMOTE_INVOKER_FIELD,iface);
+ implementsType(RpcImplementation.asCtClass)
for (ctx : metadata.contexts) {
field(ctx.routingTableField, Map)
}
]
implementMethodsFrom(RpcImplementation.asCtClass) [
- switch (name) {
- case "getSupportedInputs":
- body = '''
- {
- throw new java.lang.UnsupportedOperationException("Not implemented yet");
- return ($r) null;
- }'''
- case "invoke": {
- val tmpBody = '''
- {
- «FOR input : metadata.supportedInputs SEPARATOR " else "»
- «val rpcMetadata = metadata.rpcInputs.get(input)»
- if(«input.name».class.equals($1)) {
- return ($r) this.«rpcMetadata.methodName»((«input.name») $2);
- }
- «ENDFOR»
- throw new java.lang.IllegalArgumentException("Not supported message type");
- return ($r) null;
- }
- '''
- body = tmpBody
- }
+ body = '''
+ {
+ throw new java.lang.IllegalStateException("No provider is processing supplied message");
+ return ($r) null;
}
+ '''
]
]
- val instance = targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
- return new RpcRouterCodegenInstance(iface, instance, metadata.contexts,metadata.supportedInputs);
+ return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
+
];
- return instance;
+ return new RpcRouterCodegenInstance(iface, instance, metadata.contexts,metadata.supportedInputs);
}
private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
--- /dev/null
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator;
+import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
+
+import javassist.ClassPool;
+
+public class SingletonHolder {
+
+ public static final ClassPool CLASS_POOL = new ClassPool();
+ public static final org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator RPC_GENERATOR_IMPL = new org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator(CLASS_POOL);
+ public static final RuntimeCodeGenerator RPC_GENERATOR = RPC_GENERATOR_IMPL;
+ public static final NotificationInvokerFactory INVOKER_FACTORY = RPC_GENERATOR_IMPL.getInvokerFactory();
+}
});
return ret;
} catch (Exception e) {
- LOG.error("Could not find augmentable for {}", augmentation, e);
+ LOG.debug("Could not find augmentable for {} using {}", augmentation, augmentation.getClassLoader(), e);
return null;
}
}
import org.opendaylight.yangtools.yang.data.api.Node
import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
+import org.opendaylight.yangtools.yang.binding.RpcService
+import java.util.Set
+import org.opendaylight.yangtools.yang.common.QName
+import com.google.common.collect.FluentIterable
+import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaServiceListener, AutoCloseable {
@Property
val ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap();
+
+ @Property
+ val ConcurrentMap<Type,Set<QName>> serviceTypeToRpc = new ConcurrentHashMap();
val promisedTypeDefinitions = HashMultimap.<Type, SettableFuture<GeneratedTypeBuilder>>create;
registry.onModuleContextAdded(schemaContext, entry.key, entry.value);
binding.pathToType.putAll(entry.value.childNodes)
- //val module = entry.key;
+ val module = entry.key;
val context = entry.value;
updateBindingFor(context.childNodes, schemaContext);
updateBindingFor(context.cases, schemaContext);
+ val namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
+
+ if(!module.rpcs.empty) {
+ val rpcs = FluentIterable.from(module.rpcs).transform[QName].toSet
+ val serviceClass = new ReferencedTypeImpl(namespace,BindingGeneratorUtil.parseToClassName(module.name)+"Service");
+ serviceTypeToRpc.put(serviceClass,rpcs);
+ }
val typedefs = context.typedefs;
for (typedef : typedefs.entrySet) {
}
override dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode node) {
- return tryDeserialization[ |
- if (node == null) {
- return null;
- }
- val targetType = path.targetType
- val transformer = registry.getCodecForDataObject(targetType);
- val ret = transformer.deserialize(node)?.value as DataObject;
- return ret;
- ]
+ dataObjectFromDataDom(path.targetType,node) as DataObject;
}
override fromDataDom(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry) {
listenerRegistration = ctx.registerService(SchemaServiceListener, this, new Hashtable<String, String>());
}
}
+
+ override getRpcQNamesFor(Class<? extends RpcService> service) {
+ return serviceTypeToRpc.get(new ReferencedTypeImpl(service.package.name,service.simpleName));
+ }
private def getSchemaWithRetry(Type type) {
val typeDef = typeToSchemaNode.get(type);
override close() throws Exception {
listenerRegistration?.unregister();
}
+
+ override dataObjectFromDataDom(Class<? extends DataContainer> container, CompositeNode domData) {
+ return tryDeserialization[ |
+ if (domData == null) {
+ return null;
+ }
+ val transformer = registry.getCodecForDataObject(container);
+ val ret = transformer.deserialize(domData)?.value as DataObject;
+ return ret;
+ ]
+ }
}
val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
}
+ if (returnType.name == 'char[]') {
+ val ctCls = createUnionImplementation(inputType, typeSpec);
+ val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
+ return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ }
val ctCls = createClass(typeSpec.codecClassName) [
//staticField(Map,"AUGMENTATION_SERIALIZERS");
}
+ def createUnionImplementation(Class<?> inputType, GeneratedTransferObject typeSpec) {
+ return createClass(typeSpec.codecClassName) [
+ val properties = typeSpec.allProperties;
+ //staticField(Map,"AUGMENTATION_SERIALIZERS");
+ if (inputType.isYangBindingAvailable) {
+ implementsType(BINDING_CODEC)
+ staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
+ staticField(it, IDENTITYREF_CODEC, BindingCodec)
+ implementsType(BindingDeserializer.asCtClass)
+ }
+ method(Object, "toDomValue", Object) [
+ modifiers = PUBLIC + FINAL + STATIC
+ val ctSpec = inputType.asCtClass;
+ bodyChecked = '''
+ {
+ //System.out.println("«inputType.simpleName»#toDomValue: "+$1);
+
+ if($1 == null) {
+ return null;
+ }
+ «typeSpec.resolvedName» _value = («typeSpec.resolvedName») $1;
+ «FOR property : properties.entrySet»
+ «IF property.key != "getValue"»
+ «property.value.resolvedName» «property.key» = («property.value.resolvedName») _value.«property.key»();
+ if(«property.key» != null) {
+ return «serializeValue(property.value, property.key)»;
+ }
+ «ENDIF»
+ «ENDFOR»
+
+ return null;
+ }
+ '''
+ ]
+ method(Object, "serialize", Object) [
+ bodyChecked = '''
+ {
+ return toDomValue($1);
+ }
+ '''
+ ]
+ method(Object, "fromDomValue", Object) [
+ modifiers = PUBLIC + FINAL + STATIC
+ bodyChecked = '''
+ {
+ //System.out.println("«inputType.simpleName»#fromDomValue: "+$1);
+
+ if($1 == null) {
+ return null;
+ }
+ if($1 instanceof String) {
+ String _simpleValue = (String) $1;
+ return new «typeSpec.resolvedName»(_simpleValue.toCharArray());
+ }
+ return null;
+ }
+ '''
+ ]
+ method(Object, "deserialize", Object) [
+ bodyChecked = '''{
+ return fromDomValue($1);
+ }
+ '''
+ ]
+ ]
+ }
+
def boolean isYangBindingAvailable(Class<?> class1) {
try {
val bindingCodecClass = class1.classLoader.loadClass(BINDING_CODEC.name);
} else if (CLASS_TYPE.equals(signature)) {
return '''(«QName.resolvedName») «IDENTITYREF_CODEC».serialize(«property»)'''
}
+ if ("char[]" == signature.name) {
+ return '''new String(«property»)''';
+ }
return '''«property»''';
}
import javassist.ClassPool
import org.osgi.framework.BundleContext
import java.util.Map
-import java.util.HashMap
import javassist.LoaderClassPath
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker
import java.util.Hashtable
import java.util.WeakHashMap
import javax.annotation.concurrent.GuardedBy
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry
+import org.opendaylight.yangtools.concepts.ListenerRegistration
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry
-class BindingAwareBrokerImpl implements BindingAwareBroker, RpcProviderRegistry, AutoCloseable {
+class BindingAwareBrokerImpl extends RpcProviderRegistryImpl implements BindingAwareBroker, AutoCloseable {
private static val log = LoggerFactory.getLogger(BindingAwareBrokerImpl)
private InstanceIdentifier<? extends DataObject> root = InstanceIdentifier.builder().toInstance();
- private static val clsPool = ClassPool.getDefault()
- public static var RuntimeCodeGenerator generator;
-
- /**
- * Map of all Managed Direct Proxies
- *
- */
- private val Map<Class<? extends RpcService>, RpcProxyContext> managedProxies = new ConcurrentHashMap();
-
- /**
- *
- * Map of all available Rpc Routers
- *
- *
- */
- private val Map<Class<? extends RpcService>, RpcRouter<? extends RpcService>> rpcRouters = new WeakHashMap();
-
@Property
private var NotificationProviderService notifyBroker
@Property
var BundleContext brokerBundleContext
- ServiceRegistration<NotificationProviderService> notifyProviderRegistration
-
- ServiceRegistration<NotificationService> notifyConsumerRegistration
-
- ServiceRegistration<DataProviderService> dataProviderRegistration
-
- ServiceRegistration<DataBrokerService> dataConsumerRegistration
-
- private val proxyGenerationLock = new ReentrantLock;
-
- private val routerGenerationLock = new ReentrantLock;
-
public new(BundleContext bundleContext) {
_brokerBundleContext = bundleContext;
}
def start() {
log.info("Starting MD-SAL: Binding Aware Broker");
- initGenerator();
-
- val executor = Executors.newCachedThreadPool;
-
- // Initialization of notificationBroker
- log.info("Starting MD-SAL: Binding Aware Notification Broker");
-
- log.info("Starting MD-SAL: Binding Aware Data Broker");
-
- log.info("Starting MD-SAL: Binding Aware Data Broker");
- log.info("MD-SAL: Binding Aware Broker Started");
}
- def initGenerator() {
- // YANG Binding Class Loader
- clsPool.appendClassPath(new LoaderClassPath(RpcService.classLoader));
- generator = new RuntimeCodeGenerator(clsPool);
- }
override registerConsumer(BindingAwareConsumer consumer, BundleContext bundleCtx) {
val ctx = consumer.createContext(bundleCtx)
private def createContext(BindingAwareProvider provider, BundleContext providerCtx) {
new OsgiProviderContext(providerCtx, this)
}
-
- /**
- * Returns a Managed Direct Proxy for supplied class
- *
- * Managed direct proxy is a generated proxy class conforming to the supplied interface
- * which delegates all calls to the backing delegate.
- *
- * Proxy does not do any validation, null pointer checks or modifies data in any way, it
- * is only use to avoid exposing direct references to backing implementation of service.
- *
- * If proxy class does not exist for supplied service class it will be generated automatically.
- */
- private def <T extends RpcService> getManagedDirectProxy(Class<T> service) {
- var RpcProxyContext existing = null
-
- if ((existing = managedProxies.get(service)) != null) {
- return existing.proxy
- }
- return withLock(proxyGenerationLock) [ |
- val maybeProxy = managedProxies.get(service);
- if (maybeProxy !== null) {
- return maybeProxy.proxy;
- }
-
-
- val proxyInstance = generator.getDirectProxyFor(service)
- val rpcProxyCtx = new RpcProxyContext(proxyInstance.class)
- val properties = new Hashtable<String, String>()
- rpcProxyCtx.proxy = proxyInstance as RpcService
- properties.salServiceType = SAL_SERVICE_TYPE_CONSUMER_PROXY
- rpcProxyCtx.registration = brokerBundleContext.registerService(service, rpcProxyCtx.proxy as T, properties)
- managedProxies.put(service, rpcProxyCtx)
- return rpcProxyCtx.proxy
- ]
- }
-
- private static def <T> T withLock(ReentrantLock lock, Callable<T> method) {
- try {
- lock.lock();
- val ret = method.call;
- return ret;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Registers RPC Implementation
- *
- */
- override <T extends RpcService> addRpcImplementation(Class<T> type, T service) {
- checkNotNull(type, "Service type should not be null")
- checkNotNull(service, "Service type should not be null")
-
- val proxy = getManagedDirectProxy(type)
- checkState(proxy.delegate === null, "The Service for type %s is already registered", type)
-
- proxy.delegate = service;
- return new RpcServiceRegistrationImpl<T>(type, service, this);
- }
-
- override <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type, T service) {
- checkNotNull(type, "Service type should not be null")
- checkNotNull(service, "Service type should not be null")
-
- val router = resolveRpcRouter(type);
- checkState(router !== null)
- return new RoutedRpcRegistrationImpl<T>(service, router, this)
- }
- override <T extends RpcService> getRpcService(Class<T> service) {
- checkNotNull(service, "Service should not be null");
- return getManagedDirectProxy(service) as T;
- }
-
- private def <T extends RpcService> RpcRouter<T> resolveRpcRouter(Class<T> type) {
-
- val router = rpcRouters.get(type);
- if (router !== null) {
- return router as RpcRouter<T>;
- }
-
- // We created Router
- return withLock(routerGenerationLock) [ |
- val maybeRouter = rpcRouters.get(type);
- if (maybeRouter !== null) {
- return maybeRouter as RpcRouter<T>;
- }
-
- val newRouter = generator.getRouterFor(type);
- checkState(newRouter !== null);
- rpcRouters.put(type, newRouter);
- // We create / update Direct Proxy for router
- val proxy = getManagedDirectProxy(type);
- proxy.delegate = newRouter.invocationProxy
- return newRouter;
- ]
-
- }
-
- protected def <T extends RpcService> void registerPath(RoutedRpcRegistrationImpl<T> registration,
- Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> path) {
-
- val router = registration.router;
- val paths = registration.registeredPaths;
-
- val routingTable = router.getRoutingTable(context)
- checkState(routingTable != null);
-
- // Updating internal structure of registration
- routingTable.updateRoute(path, registration.instance)
-
- // Update routing table / send announce to message bus
- val success = paths.put(context, path);
- }
-
- protected def <T extends RpcService> void unregisterPath(RoutedRpcRegistrationImpl<T> registration,
- Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> path) {
-
- val router = registration.router;
- val paths = registration.registeredPaths;
-
- val routingTable = router.getRoutingTable(context)
- checkState(routingTable != null);
-
- // Updating internal structure of registration
- val target = routingTable.getRoute(path)
- checkState(target === registration.instance)
- routingTable.removeRoute(path)
- checkState(paths.remove(context, path));
- }
-
- protected def <T extends RpcService> void unregisterRoutedRpcService(RoutedRpcRegistrationImpl<T> registration) {
-
- val router = registration.router;
- val paths = registration.registeredPaths;
-
- for (ctxMap : registration.registeredPaths.entries) {
- val context = ctxMap.key
- val routingTable = router.getRoutingTable(context)
- val path = ctxMap.value
- routingTable.removeRoute(path)
- }
- }
-
- protected def <T extends RpcService> void unregisterRpcService(RpcServiceRegistrationImpl<T> registration) {
-
- val type = registration.serviceType;
-
- val proxy = managedProxies.get(type);
- if (proxy.proxy.delegate === registration.instance) {
- proxy.proxy.delegate = null;
- }
- }
-
- def createDelegate(Class<? extends RpcService> type) {
- getManagedDirectProxy(type);
- }
-
- def getRpcRouters() {
- return Collections.unmodifiableMap(rpcRouters);
- }
-
- override close() {
- dataConsumerRegistration.unregister()
- dataProviderRegistration.unregister()
- notifyConsumerRegistration.unregister()
- notifyProviderRegistration.unregister()
- }
-
-}
-
-class RoutedRpcRegistrationImpl<T extends RpcService> extends AbstractObjectRegistration<T> implements RoutedRpcRegistration<T> {
-
- @Property
- private val BindingAwareBrokerImpl broker;
-
- @Property
- private val RpcRouter<T> router;
-
- @Property
- private val Multimap<Class<? extends BaseIdentity>, InstanceIdentifier<?>> registeredPaths = HashMultimap.create();
-
- private var closed = false;
-
- new(T instance, RpcRouter<T> backingRouter, BindingAwareBrokerImpl broker) {
- super(instance)
- _router = backingRouter;
- _broker = broker;
- }
-
- override protected removeRegistration() {
- closed = true
- broker.unregisterRoutedRpcService(this)
- }
-
- override registerInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> instance) {
- registerPath(context, instance);
- }
-
- override unregisterInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> instance) {
- unregisterPath(context, instance);
- }
-
- override registerPath(Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> path) {
- checkClosed()
- broker.registerPath(this, context, path);
- }
-
- override unregisterPath(Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> path) {
- checkClosed()
- broker.unregisterPath(this, context, path);
- }
-
- override getServiceType() {
- return router.serviceType;
- }
-
- private def checkClosed() {
- if (closed)
- throw new IllegalStateException("Registration was closed.");
- }
-
-}
-
-class RpcServiceRegistrationImpl<T extends RpcService> extends AbstractObjectRegistration<T> implements RpcRegistration<T> {
-
- private var BindingAwareBrokerImpl broker;
-
- @Property
- val Class<T> serviceType;
-
- public new(Class<T> type, T service, BindingAwareBrokerImpl broker) {
- super(service);
- this._serviceType = type;
- this.broker = broker;
- }
-
- override protected removeRegistration() {
- broker.unregisterRpcService(this);
- broker = null;
+ override close() throws Exception {
+
}
-}
+}
\ No newline at end of file
import org.opendaylight.yangtools.concepts.ListenerRegistration\r
import org.opendaylight.yangtools.concepts.Registration\r
import org.opendaylight.yangtools.yang.binding.Notification\r
-import org.slf4j.LoggerFactory\r
-\r
+import org.slf4j.LoggerFactory\rimport org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder
+
class NotificationBrokerImpl implements NotificationProviderService, AutoCloseable {\r
\r
val Multimap<Class<? extends Notification>, NotificationListener<?>> listeners;\r
@Property\r
var ExecutorService executor;\r
\r
+ new() {\r
+ listeners = HashMultimap.create()\r
+ }\r
+\r
+ @Deprecated\r
new(ExecutorService executor) {\r
listeners = HashMultimap.create()\r
this.executor = executor;\r
\r
override registerNotificationListener(\r
org.opendaylight.yangtools.yang.binding.NotificationListener listener) {\r
- val invoker = BindingAwareBrokerImpl.generator.invokerFactory.invokerFor(listener);\r
+ val invoker = SingletonHolder.INVOKER_FACTORY.invokerFor(listener);\r
for (notifyType : invoker.supportedNotifications) {\r
listeners.put(notifyType, invoker.invocationProxy)\r
}\r
val ref = services.iterator().next() as ServiceReference<T>;
return bundleContext.getService(ref) as T;
} else {
- broker.createDelegate(module);
- return getRpcService(module);
+ return broker.getRpcService(module);
}
} catch (InvalidSyntaxException e) {
log.error("Created filter was invalid:", e.message, e)
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import javax.swing.tree.ExpandVetoException;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
+import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator;
+import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper;
+import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
+import org.opendaylight.controller.sal.binding.spi.RpcContextIdentifier;
+import org.opendaylight.controller.sal.binding.spi.RpcRouter;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+import static com.google.common.base.Preconditions.*;
+
+public class RpcProviderRegistryImpl implements //
+ RpcProviderRegistry, //
+ RouteChangePublisher<RpcContextIdentifier, InstanceIdentifier<?>> {
+
+ private RuntimeCodeGenerator rpcFactory = SingletonHolder.RPC_GENERATOR_IMPL;
+
+ private final Map<Class<? extends RpcService>, RpcService> publicProxies = new WeakHashMap<>();
+ private final Map<Class<? extends RpcService>, RpcRouter<?>> rpcRouters = new WeakHashMap<>();
+ private final ListenerRegistry<RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> routeChangeListeners = ListenerRegistry
+ .create();
+
+ @Override
+ public final <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type,
+ T implementation) throws IllegalStateException {
+ return getRpcRouter(type).addRoutedRpcImplementation(implementation);
+ }
+
+ @Override
+ public final <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> type, T implementation)
+ throws IllegalStateException {
+ RpcRouter<T> potentialRouter = (RpcRouter<T>) rpcRouters.get(type);
+ if (potentialRouter != null) {
+ checkState(potentialRouter.getDefaultService() == null,
+ "Default service for routed RPC already registered.");
+ return potentialRouter.registerDefaultService(implementation);
+ }
+ T publicProxy = getRpcService(type);
+ RpcService currentDelegate = RuntimeCodeHelper.getDelegate(publicProxy);
+ checkState(currentDelegate == null, "Rpc service is already registered");
+ RuntimeCodeHelper.setDelegate(publicProxy, implementation);
+ return new RpcProxyRegistration<T>(type, implementation, this);
+ }
+
+ @Override
+ public final <T extends RpcService> T getRpcService(Class<T> type) {
+
+ RpcService potentialProxy = publicProxies.get(type);
+ if (potentialProxy != null) {
+ return (T) potentialProxy;
+ }
+ T proxy = rpcFactory.getDirectProxyFor(type);
+ publicProxies.put(type, proxy);
+ return proxy;
+ }
+
+ private <T extends RpcService> RpcRouter<T> getRpcRouter(Class<T> type) {
+ RpcRouter<?> potentialRouter = rpcRouters.get(type);
+ if (potentialRouter != null) {
+ return (RpcRouter<T>) potentialRouter;
+ }
+ RpcRouter<T> router = rpcFactory.getRouterFor(type);
+ router.registerRouteChangeListener(new RouteChangeForwarder(type));
+ RuntimeCodeHelper.setDelegate(getRpcService(type), router.getInvocationProxy());
+ rpcRouters.put(type, router);
+ return router;
+ }
+
+ public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+ L listener) {
+ return (ListenerRegistration<L>) routeChangeListeners.register(listener);
+ }
+
+ public RuntimeCodeGenerator getRpcFactory() {
+ return rpcFactory;
+ }
+
+ public void setRpcFactory(RuntimeCodeGenerator rpcFactory) {
+ this.rpcFactory = rpcFactory;
+ }
+
+ private class RouteChangeForwarder<T extends RpcService> implements
+ RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
+
+ private final Class<T> type;
+
+ public RouteChangeForwarder(Class<T> type) {
+ this.type = type;
+ }
+
+ @Override
+ public void onRouteChange(RouteChange<Class<? extends BaseIdentity>, InstanceIdentifier<?>> change) {
+ Map<RpcContextIdentifier, Set<InstanceIdentifier<?>>> announcements = new HashMap<>();
+ for (Entry<Class<? extends BaseIdentity>, Set<InstanceIdentifier<?>>> entry : change.getAnnouncements()
+ .entrySet()) {
+ RpcContextIdentifier key = RpcContextIdentifier.contextFor(type, entry.getKey());
+ announcements.put(key, entry.getValue());
+ }
+ Map<RpcContextIdentifier, Set<InstanceIdentifier<?>>> removals = new HashMap<>();
+ for (Entry<Class<? extends BaseIdentity>, Set<InstanceIdentifier<?>>> entry : change.getRemovals()
+ .entrySet()) {
+ RpcContextIdentifier key = RpcContextIdentifier.contextFor(type, entry.getKey());
+ removals.put(key, entry.getValue());
+ }
+ RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> toPublish = RoutingUtils
+ .<RpcContextIdentifier, InstanceIdentifier<?>> change(announcements, removals);
+ for (ListenerRegistration<RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> listener : routeChangeListeners) {
+ try {
+ listener.getInstance().onRouteChange(toPublish);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public static class RpcProxyRegistration<T extends RpcService> extends AbstractObjectRegistration<T> implements
+ RpcRegistration<T> {
+
+ private final Class<T> serviceType;
+ private RpcProviderRegistryImpl registry;
+
+ public RpcProxyRegistration(Class<T> type, T service, RpcProviderRegistryImpl registry) {
+ super(service);
+ serviceType = type;
+ }
+
+ @Override
+ public Class<T> getServiceType() {
+ return serviceType;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ if (registry != null) {
+ T publicProxy = registry.getRpcService(serviceType);
+ RpcService currentDelegate = RuntimeCodeHelper.getDelegate(publicProxy);
+ if (currentDelegate == getInstance()) {
+ RuntimeCodeHelper.setDelegate(publicProxy, null);
+ }
+ registry = null;
+ }
+ }
+ }
+}
package org.opendaylight.controller.sal.binding.impl.connect.dom;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+
import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider;
+import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;
+import org.opendaylight.controller.sal.binding.spi.RpcContextIdentifier;
+import org.opendaylight.controller.sal.binding.spi.RpcRouter;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.util.ClassLoaderUtils;
import org.opendaylight.yangtools.yang.binding.Augmentable;
import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcInput;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class BindingIndependentDataServiceConnector implements //
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
+
+import static com.google.common.base.Preconditions.*;
+import static org.opendaylight.yangtools.concepts.util.ClassLoaderUtils.*;
+
+public class BindingIndependentConnector implements //
RuntimeDataProvider, //
- Provider, AutoCloseable {
+ Provider, //
+ AutoCloseable {
- private final Logger LOG = LoggerFactory.getLogger(BindingIndependentDataServiceConnector.class);
+ private final Logger LOG = LoggerFactory.getLogger(BindingIndependentConnector.class);
private static final InstanceIdentifier<? extends DataObject> ROOT = InstanceIdentifier.builder().toInstance();
private Registration<DataCommitHandler<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode>> biCommitHandlerRegistration;
+ private RpcProvisionRegistry biRpcRegistry;
+ private RpcProviderRegistryImpl baRpcRegistry;
+
+ private ListenerRegistration<DomToBindingRpcForwardingManager> domToBindingRpcManager;
+ // private ListenerRegistration<BindingToDomRpcForwardingManager>
+ // bindingToDomRpcManager;
+
+ private Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> toDOMInstanceIdentifier = new Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier>() {
+
+ @Override
+ public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(InstanceIdentifier<?> input) {
+ return mappingService.toDataDom(input);
+ }
+
+ };
+
@Override
public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path) {
try {
org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
-
-
+
CompositeNode result = biDataService.readOperationalData(biPath);
Class<? extends DataObject> targetType = path.getTargetType();
-
- if(Augmentation.class.isAssignableFrom(targetType)) {
+
+ if (Augmentation.class.isAssignableFrom(targetType)) {
path = mappingService.fromDataDom(biPath);
Class<? extends Augmentation<?>> augmentType = (Class<? extends Augmentation<?>>) targetType;
DataObject parentTo = mappingService.dataObjectFromDataDom(path, result);
- if(parentTo instanceof Augmentable<?>) {
+ if (parentTo instanceof Augmentable<?>) {
return (DataObject) ((Augmentable) parentTo).getAugmentation(augmentType);
}
-
+
}
return mappingService.dataObjectFromDataDom(path, result);
-
+
} catch (DeserializationException e) {
throw new IllegalStateException(e);
}
this.baDataService = baDataService;
}
+ public RpcProviderRegistry getRpcRegistry() {
+ return baRpcRegistry;
+ }
+
+ public void setRpcRegistry(RpcProviderRegistryImpl rpcRegistry) {
+ this.baRpcRegistry = rpcRegistry;
+ }
+
public void start() {
baDataService.registerDataReader(ROOT, this);
baCommitHandlerRegistration = baDataService.registerCommitHandler(ROOT, bindingToDomCommitHandler);
biCommitHandlerRegistration = biDataService.registerCommitHandler(ROOT_BI, domToBindingCommitHandler);
baDataService.registerCommitHandlerListener(domToBindingCommitHandler);
+
+ if (baRpcRegistry != null && biRpcRegistry != null) {
+ domToBindingRpcManager = baRpcRegistry.registerRouteChangeListener(new DomToBindingRpcForwardingManager());
+
+ }
}
public void setMappingService(BindingIndependentMappingService mappingService) {
start();
}
+ public <T extends RpcService> void onRpcRouterCreated(Class<T> serviceType, RpcRouter<T> router) {
+
+ }
+
+ public void setDomRpcRegistry(RpcProvisionRegistry registry) {
+ biRpcRegistry = registry;
+ }
+
@Override
public void close() throws Exception {
if (baCommitHandlerRegistration != null) {
return forwardedTransaction;
}
}
+
+ private class DomToBindingRpcForwardingManager implements
+ RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>> {
+
+ private final Map<Class<? extends RpcService>, DomToBindingRpcForwarder> forwarders = new WeakHashMap<>();
+
+ @Override
+ public void onRouteChange(RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> change) {
+ for (Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry : change.getAnnouncements().entrySet()) {
+ bindingRoutesAdded(entry);
+ }
+ }
+
+ private void bindingRoutesAdded(Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry) {
+ Class<? extends BaseIdentity> context = entry.getKey().getRoutingContext();
+ Class<? extends RpcService> service = entry.getKey().getRpcService();
+ if (context != null) {
+ getRpcForwarder(service, context).registerPaths(context, service, entry.getValue());
+ }
+ }
+
+ private DomToBindingRpcForwarder getRpcForwarder(Class<? extends RpcService> service,
+ Class<? extends BaseIdentity> context) {
+ DomToBindingRpcForwarder potential = forwarders.get(service);
+ if (potential != null) {
+ return potential;
+ }
+ if (context == null) {
+ potential = new DomToBindingRpcForwarder(service);
+ } else {
+ potential = new DomToBindingRpcForwarder(service, context);
+ }
+ forwarders.put(service, potential);
+ return potential;
+ }
+
+ }
+
+ private class DomToBindingRpcForwarder implements RpcImplementation {
+
+ private final Set<QName> supportedRpcs;
+ private final WeakReference<Class<? extends RpcService>> rpcServiceType;
+ private Set<org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration> registrations;
+
+ public DomToBindingRpcForwarder(Class<? extends RpcService> service) {
+ this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
+ this.supportedRpcs = mappingService.getRpcQNamesFor(service);
+ for (QName rpc : supportedRpcs) {
+ biRpcRegistry.addRpcImplementation(rpc, this);
+ }
+ registrations = ImmutableSet.of();
+ }
+
+ public DomToBindingRpcForwarder(Class<? extends RpcService> service, Class<? extends BaseIdentity> context) {
+ this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
+ this.supportedRpcs = mappingService.getRpcQNamesFor(service);
+ registrations = new HashSet<>();
+ for (QName rpc : supportedRpcs) {
+ registrations.add(biRpcRegistry.addRoutedRpcImplementation(rpc, this));
+ }
+ registrations = ImmutableSet.copyOf(registrations);
+ }
+
+ public void registerPaths(Class<? extends BaseIdentity> context, Class<? extends RpcService> service,
+ Set<InstanceIdentifier<?>> set) {
+ QName ctx = BindingReflections.findQName(context);
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
+ toDOMInstanceIdentifier)) {
+ for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
+ reg.registerPath(ctx, path);
+ }
+ }
+ }
+
+ public void removePaths(Class<? extends BaseIdentity> context, Class<? extends RpcService> service,
+ Set<InstanceIdentifier<?>> set) {
+ QName ctx = BindingReflections.findQName(context);
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
+ toDOMInstanceIdentifier)) {
+ for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
+ reg.unregisterPath(ctx, path);
+ }
+ }
+ }
+
+ @Override
+ public Set<QName> getSupportedRpcs() {
+ return supportedRpcs;
+ }
+
+ @Override
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode domInput) {
+ checkArgument(rpc != null);
+ checkArgument(domInput != null);
+
+ Class<? extends RpcService> rpcType = rpcServiceType.get();
+ checkState(rpcType != null);
+ RpcService rpcService = baRpcRegistry.getRpcService(rpcType);
+ checkState(rpcService != null);
+ CompositeNode domUnwrappedInput = domInput.getFirstCompositeByName(QName.create(rpc, "input"));
+ try {
+ return resolveInvocationStrategy(rpc, rpcType).invokeOn(rpcService, domUnwrappedInput);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private RpcInvocationStrategy resolveInvocationStrategy(final QName rpc,
+ final Class<? extends RpcService> rpcType) throws Exception {
+ return ClassLoaderUtils.withClassLoader(rpcType.getClassLoader(), new Callable<RpcInvocationStrategy>() {
+ @Override
+ public RpcInvocationStrategy call() throws Exception {
+ String methodName = BindingMapping.getMethodName(rpc);
+ Method targetMethod = null;
+ for (Method possibleMethod : rpcType.getMethods()) {
+ if (possibleMethod.getName().equals(methodName)
+ && BindingReflections.isRpcMethod(possibleMethod)) {
+ targetMethod = possibleMethod;
+ break;
+ }
+ }
+ checkState(targetMethod != null, "Rpc method not found");
+ Optional<Class<?>> outputClass = BindingReflections.resolveRpcOutputClass(targetMethod);
+ Optional<Class<? extends DataContainer>> inputClass = BindingReflections
+ .resolveRpcInputClass(targetMethod);
+
+ RpcInvocationStrategy strategy = null;
+ if (outputClass.isPresent()) {
+ if (inputClass.isPresent()) {
+ strategy = new DefaultInvocationStrategy(targetMethod, outputClass.get(), inputClass.get());
+ } else {
+ strategy = new NoInputNoOutputInvocationStrategy(targetMethod);
+ }
+ } else {
+ strategy = null;
+ }
+ return strategy;
+ }
+
+ });
+ }
+ }
+
+ private abstract class RpcInvocationStrategy {
+
+ protected final Method targetMethod;
+
+ public RpcInvocationStrategy(Method targetMethod) {
+ this.targetMethod = targetMethod;
+ }
+
+ public abstract RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput)
+ throws Exception;
+
+ public RpcResult<CompositeNode> invokeOn(RpcService rpcService, CompositeNode domInput) throws Exception {
+ return uncheckedInvoke(rpcService, domInput);
+ }
+ }
+
+ private class DefaultInvocationStrategy extends RpcInvocationStrategy {
+
+ @SuppressWarnings("rawtypes")
+ private WeakReference<Class> inputClass;
+
+ @SuppressWarnings("rawtypes")
+ private WeakReference<Class> outputClass;
+
+ public DefaultInvocationStrategy(Method targetMethod, Class<?> outputClass,
+ Class<? extends DataContainer> inputClass) {
+ super(targetMethod);
+ this.outputClass = new WeakReference(outputClass);
+ this.inputClass = new WeakReference(inputClass);
+ }
+
+ @Override
+ public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
+ DataContainer bindingInput = mappingService.dataObjectFromDataDom(inputClass.get(), domInput);
+ Future<RpcResult<?>> result = (Future<RpcResult<?>>) targetMethod.invoke(rpcService, bindingInput);
+ if (result == null) {
+ return Rpcs.getRpcResult(false);
+ }
+ RpcResult<?> bindingResult = result.get();
+ return Rpcs.getRpcResult(true);
+ }
+
+ }
+
+ private class NoInputNoOutputInvocationStrategy extends RpcInvocationStrategy {
+
+ public NoInputNoOutputInvocationStrategy(Method targetMethod) {
+ super(targetMethod);
+ }
+
+ public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
+ Future<RpcResult<Void>> result = (Future<RpcResult<Void>>) targetMethod.invoke(rpcService);
+ RpcResult<Void> bindingResult = result.get();
+ return Rpcs.getRpcResult(bindingResult.isSuccessful(), bindingResult.getErrors());
+ }
+
+ }
}
package org.opendaylight.controller.sal.binding.impl.connect.dom;
import java.util.Map.Entry;
+import java.util.Set;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
public interface BindingIndependentMappingService {
DataObject dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode result) throws DeserializationException;
InstanceIdentifier<?> fromDataDom(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry) throws DeserializationException;
+
+ Set<QName> getRpcQNamesFor(Class<? extends RpcService> service);
+
+ DataContainer dataObjectFromDataDom(Class<? extends DataContainer> inputClass, CompositeNode domInput);
}
public static Class<?> loadClassWithTCCL(String name) throws ClassNotFoundException {
if ("byte[]".equals(name)) {
return byte[].class;
+ } else if("char[]".equals(name)) {
+ return char[].class;
}
try {
return Thread.currentThread().getContextClassLoader().loadClass(name);
try {
return loadClassWithTCCL(fullyQualifiedName);
} catch (ClassNotFoundException e) {
-
+ return null;
}
- return null;
}
}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.binding.spi;
+
+public class RoutingContext {
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.spi;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public final class RpcContextIdentifier implements Immutable{
+
+ public final Class<? extends RpcService> rpcService;
+ public final Class<? extends BaseIdentity> routingContext;
+
+ private RpcContextIdentifier(Class<? extends RpcService> rpcService, Class<? extends BaseIdentity> routingContext) {
+ super();
+ this.rpcService = rpcService;
+ this.routingContext = routingContext;
+ }
+
+ public Class<? extends RpcService> getRpcService() {
+ return rpcService;
+ }
+
+ public Class<? extends BaseIdentity> getRoutingContext() {
+ return routingContext;
+ }
+
+ public static final RpcContextIdentifier contextForGlobalRpc(Class<? extends RpcService> serviceType) {
+ return new RpcContextIdentifier(serviceType, null);
+ }
+
+ public static final RpcContextIdentifier contextFor(Class<? extends RpcService> serviceType,Class<? extends BaseIdentity> routingContext) {
+ return new RpcContextIdentifier(serviceType, routingContext);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((routingContext == null) ? 0 : routingContext.hashCode());
+ result = prime * result + ((rpcService == null) ? 0 : rpcService.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RpcContextIdentifier other = (RpcContextIdentifier) obj;
+ if (routingContext == null) {
+ if (other.routingContext != null)
+ return false;
+ } else if (!routingContext.equals(other.routingContext))
+ return false;
+ if (rpcService == null) {
+ if (other.rpcService != null)
+ return false;
+ } else if (!rpcService.equals(other.rpcService))
+ return false;
+ return true;
+ }
+
+}
import java.util.Set;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.RpcImplementation;
* Type of RpcService for which router provides routing information
* and route selection.
*/
-public interface RpcRouter<T extends RpcService> extends RpcImplementation{
+public interface RpcRouter<T extends RpcService> extends //
+ RouteChangePublisher<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
/**
* Returns a type of RpcService which is served by this instance of router.
* @return default instance responsible for processing RPCs.
*/
T getDefaultService();
-
- /**
- *
- */
- void setDefaultService(T service);
Set<Class<? extends BaseIdentity>> getContexts();
+
+ RoutedRpcRegistration<T> addRoutedRpcImplementation(T service);
+
+ RpcRegistration<T> registerDefaultService(T service);
}
import org.junit.Before;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl;
-import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentDataServiceConnector;
+import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector;
import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService;
import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory;
import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
assertNotNull(product);
assertNotNull(product.getInvocationProxy());
- assertNotNull(product.getSupportedInputs());
- assertTrue(product.getSupportedInputs().contains(SimpleInput.class));
- assertTrue(product.getSupportedInputs().contains(InheritedContextInput.class));
assertEquals("2 fields should be generated.", 2, product.getInvocationProxy().getClass().getFields().length);
verifyRouting(product);
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.bugfix;
+
+import org.junit.Test;
+import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.node.attributes.igp.node.attributes.Prefix;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.node.attributes.igp.node.attributes.PrefixBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+
+
+
+
+
+
+
+import static org.junit.Assert.*;
+
+public class UnionSerializationTest extends AbstractDataServiceTest {
+
+ public static final String PREFIX_STRING = "192.168.0.1/32";
+
+
+ @Test
+ public void testPrefixSerialization() throws Exception {
+
+ Ipv4Prefix ipv4prefix = new Ipv4Prefix(PREFIX_STRING);
+ IpPrefix ipPrefix = new IpPrefix(ipv4prefix);
+ Prefix prefix = new PrefixBuilder().setPrefix(ipPrefix).build();
+
+ CompositeNode serialized = testContext.getBindingToDomMappingService().toDataDom(prefix);
+ assertNotNull(serialized);
+ assertNotNull(serialized.getFirstSimpleByName(Prefix.QNAME));
+ assertEquals(PREFIX_STRING, serialized.getFirstSimpleByName(Prefix.QNAME).getValue());
+
+ Prefix deserialized = (Prefix) testContext.getBindingToDomMappingService().dataObjectFromDataDom(InstanceIdentifier.builder().node(Prefix.class).build(), serialized);
+ assertNotNull(deserialized);
+ assertNotNull(deserialized.getPrefix());
+ assertNotNull(deserialized.getPrefix().getIpv4Prefix());
+ assertEquals(PREFIX_STRING, deserialized.getPrefix().getIpv4Prefix().getValue());
+ }
+
+}
import javassist.ClassPool;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.dom.serializer.impl.RuntimeGeneratedMappingServiceImpl;
+import org.opendaylight.controller.sal.binding.impl.BindingAwareBrokerImpl;
import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl;
-import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentDataServiceConnector;
+import org.opendaylight.controller.sal.binding.impl.NotificationBrokerImpl;
+import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector;
import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.opendaylight.controller.sal.core.api.data.DataStore;
+import org.opendaylight.controller.sal.dom.broker.BrokerImpl;
import org.opendaylight.controller.sal.dom.broker.impl.DataStoreStatsWrapper;
import org.opendaylight.controller.sal.dom.broker.impl.HashMapDataStore;
+import org.opendaylight.controller.sal.dom.broker.impl.RpcRouterImpl;
import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import static com.google.common.base.Preconditions.*;
-public class BindingTestContext {
+public class BindingTestContext implements AutoCloseable {
public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier TREE_ROOT = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
private static final Logger LOG = LoggerFactory.getLogger(BindingTestContext.class);
private RuntimeGeneratedMappingServiceImpl mappingServiceImpl;
+
+
+ private BindingAwareBrokerImpl baBrokerImpl;
private DataBrokerImpl baDataImpl;
+ private NotificationBrokerImpl baNotifyImpl;
+ private BindingIndependentConnector baConnectDataServiceImpl;
+
private org.opendaylight.controller.sal.dom.broker.DataBrokerImpl biDataImpl;
-
- private BindingIndependentDataServiceConnector connectorServiceImpl;
+ private BrokerImpl biBrokerImpl;
private HashMapDataStore rawDataStore;
private SchemaAwareDataStoreAdapter schemaAwareDataStore;
private DataStoreStatsWrapper dataStoreStats;
private final ClassPool classPool;
private final boolean startWithSchema;
+
protected BindingTestContext(ListeningExecutorService executor, ClassPool classPool, boolean startWithSchema) {
this.executor = executor;
baDataImpl.setExecutor(executor);
}
+ public void startBindingBroker() {
+ checkState(executor != null,"Executor needs to be set");
+ checkState(baDataImpl != null,"Binding Data Broker must be started");
+ checkState(baNotifyImpl != null, "Notification Service must be started");
+ baBrokerImpl = new BindingAwareBrokerImpl(null);
+
+ baBrokerImpl.setDataBroker(baDataImpl);
+ baBrokerImpl.setNotifyBroker(baNotifyImpl);
+
+ baBrokerImpl.start();
+ }
+
public void startBindingToDomDataConnector() {
checkState(baDataImpl != null,"Binding Data Broker needs to be started");
checkState(biDataImpl != null,"DOM Data Broker needs to be started.");
checkState(mappingServiceImpl != null,"DOM Mapping Service needs to be started.");
- connectorServiceImpl = new BindingIndependentDataServiceConnector();
- connectorServiceImpl.setBaDataService(baDataImpl);
- connectorServiceImpl.setBiDataService(biDataImpl);
- connectorServiceImpl.setMappingService(mappingServiceImpl);
- connectorServiceImpl.start();
+ baConnectDataServiceImpl = new BindingIndependentConnector();
+ baConnectDataServiceImpl.setRpcRegistry(baBrokerImpl);
+ baConnectDataServiceImpl.setDomRpcRegistry(getDomRpcRegistry());
+ baConnectDataServiceImpl.setBaDataService(baDataImpl);
+ baConnectDataServiceImpl.setBiDataService(biDataImpl);
+ baConnectDataServiceImpl.setMappingService(mappingServiceImpl);
+ baConnectDataServiceImpl.start();
}
public void startBindingToDomMappingService() {
public void start() {
startBindingDataBroker();
+ startBindingNotificationBroker();
+ startBindingBroker();
startDomDataBroker();
startDomDataStore();
+ startDomBroker();
startBindingToDomMappingService();
startBindingToDomDataConnector();
if(startWithSchema) {
}
}
+ private void startDomBroker() {
+ checkState(executor != null);
+ biBrokerImpl = new BrokerImpl();
+ biBrokerImpl.setExecutor(executor);
+ biBrokerImpl.setRouter(new RpcRouterImpl("test"));
+ }
+
+ public void startBindingNotificationBroker() {
+ checkState(executor != null);
+ baNotifyImpl = new NotificationBrokerImpl(executor);
+
+ }
+
public void loadYangSchemaFromClasspath() {
String[] files = getAllYangFilesOnClasspath();
updateYangSchema(files);
dataStoreStats.getRequestCommitCount(), dataStoreStats.getRequestCommitTotalTime(),
dataStoreStats.getRequestCommitAverageTime());
}
+
+ public RpcProviderRegistry getBindingRpcRegistry() {
+ return baBrokerImpl;
+ }
+
+ public RpcProvisionRegistry getDomRpcRegistry() {
+ if(biBrokerImpl == null) {
+ return null;
+ }
+ return biBrokerImpl.getRouter();
+ }
+
+ public RpcImplementation getDomRpcInvoker() {
+ return biBrokerImpl.getRouter();
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
}
<developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
<url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
</scm>
-
<build>
<plugins>
<plugin>
</build>
<dependencies>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-native</artifactId>
+ <scope>test</scope>
+ <version>${exam.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-broker-impl</artifactId>
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.connect.dom;
+
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.concurrent.Future;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory;
+import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeFlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103.TransactionId;
+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.inventory.rev130819.NodeContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+
+import static junit.framework.Assert.*;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class CrossBrokerRpcTest {
+
+ protected RpcProviderRegistry baRpcRegistry;
+ protected RpcProvisionRegistry biRpcRegistry;
+ private BindingTestContext testContext;
+ private RpcImplementation biRpcInvoker;
+ private MessageCapturingFlowService flowService;
+
+ public static final NodeId NODE_A = new NodeId("a");
+ public static final NodeId NODE_B = new NodeId("b");
+ public static final NodeId NODE_C = new NodeId("c");
+ public static final NodeId NODE_D = new NodeId("d");
+
+ public static final InstanceIdentifier<Node> BA_NODE_A_ID = createBANodeIdentifier(NODE_A);
+ public static final InstanceIdentifier<Node> BA_NODE_B_ID = createBANodeIdentifier(NODE_B);
+ public static final InstanceIdentifier<Node> BA_NODE_C_ID = createBANodeIdentifier(NODE_C);
+ public static final InstanceIdentifier<Node> BA_NODE_D_ID = createBANodeIdentifier(NODE_D);
+
+ public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier BI_NODE_A_ID = createBINodeIdentifier(NODE_A);
+ public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier BI_NODE_B_ID = createBINodeIdentifier(NODE_B);
+ public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier BI_NODE_C_ID = createBINodeIdentifier(NODE_C);
+ public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier BI_NODE_D_ID = createBINodeIdentifier(NODE_D);
+
+ private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
+ private static final QName ADD_FLOW_QNAME = QName.create(NodeFlowRemoved.QNAME, "add-flow");
+ private static final QName REMOVE_FLOW_QNAME = QName.create(NodeFlowRemoved.QNAME, "remove-flow");
+ private static final QName UPDATE_FLOW_QNAME = QName.create(NodeFlowRemoved.QNAME, "update-flow");
+
+ @Before
+ public void setup() {
+ BindingBrokerTestFactory testFactory = new BindingBrokerTestFactory();
+ testFactory.setExecutor(MoreExecutors.sameThreadExecutor());
+ testFactory.setStartWithParsedSchema(true);
+ testContext = testFactory.getTestContext();
+
+ testContext.start();
+ baRpcRegistry = testContext.getBindingRpcRegistry();
+ biRpcRegistry = testContext.getDomRpcRegistry();
+ biRpcInvoker = testContext.getDomRpcInvoker();
+ assertNotNull(baRpcRegistry);
+ assertNotNull(biRpcRegistry);
+
+ flowService = MessageCapturingFlowService.create(baRpcRegistry);
+
+ }
+
+ @Test
+ public void bindingRoutedRpcProvider_DomInvokerTest() {
+
+ flowService//
+ .registerPath(NodeContext.class, BA_NODE_A_ID) //
+ .registerPath(NodeContext.class, BA_NODE_B_ID) //
+ .setAddFlowResult(addFlowResult(true, 10));
+
+ SalFlowService baFlowInvoker = baRpcRegistry.getRpcService(SalFlowService.class);
+ assertNotSame(flowService, baFlowInvoker);
+
+ AddFlowInput addFlowA = addFlow(BA_NODE_A_ID) //
+ .setPriority(100).setBarrier(true).build();
+
+ CompositeNode addFlowDom = toDomRpc(ADD_FLOW_QNAME, addFlowA);
+ assertNotNull(addFlowDom);
+ RpcResult<CompositeNode> domResult = biRpcInvoker.invokeRpc(ADD_FLOW_QNAME, addFlowDom);
+ assertNotNull(domResult);
+ assertTrue("DOM result is successful.", domResult.isSuccessful());
+ assertTrue("Bidning Add Flow RPC was captured.", flowService.getReceivedAddFlows().containsKey(BA_NODE_A_ID));
+ assertEquals(addFlowA, flowService.getReceivedAddFlows().get(BA_NODE_A_ID).iterator().next());
+ }
+
+ public void bindingRpcInvoker_DomRoutedProviderTest() {
+
+ }
+
+ private CompositeNode toDomRpcInput(DataObject addFlowA) {
+ return testContext.getBindingToDomMappingService().toDataDom(addFlowA);
+ }
+
+ @After
+ public void teardown() throws Exception {
+ testContext.close();
+ }
+
+ private static InstanceIdentifier<Node> createBANodeIdentifier(NodeId node) {
+ return InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(node)).toInstance();
+ }
+
+ private static org.opendaylight.yangtools.yang.data.api.InstanceIdentifier createBINodeIdentifier(NodeId node) {
+ return org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder().node(Nodes.QNAME)
+ .nodeWithKey(Node.QNAME, NODE_ID_QNAME, node.getValue()).toInstance();
+ }
+
+ private Future<RpcResult<AddFlowOutput>> addFlowResult(boolean success, long xid) {
+ AddFlowOutput output = new AddFlowOutputBuilder() //
+ .setTransactionId(new TransactionId(BigInteger.valueOf(xid))).build();
+ RpcResult<AddFlowOutput> result = Rpcs.getRpcResult(success, output, ImmutableList.<RpcError> of());
+ return Futures.immediateFuture(result);
+ }
+
+ private static AddFlowInputBuilder addFlow(InstanceIdentifier<Node> nodeId) {
+ AddFlowInputBuilder builder = new AddFlowInputBuilder();
+ builder.setNode(new NodeRef(nodeId));
+ return builder;
+ }
+
+ private CompositeNode toDomRpc(QName rpcName, AddFlowInput addFlowA) {
+ return new CompositeNodeTOImpl(rpcName, null,
+ Collections.<org.opendaylight.yangtools.yang.data.api.Node<?>> singletonList(toDomRpcInput(addFlowA)));
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.connect.dom;
+
+import static junit.framework.Assert.assertNotNull;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+public class MessageCapturingFlowService implements SalFlowService, AutoCloseable {
+
+ private Future<RpcResult<AddFlowOutput>> addFlowResult;
+ private Future<RpcResult<RemoveFlowOutput>> removeFlowResult;
+ private Future<RpcResult<UpdateFlowOutput>> updateFlowResult;
+
+ private final Multimap<InstanceIdentifier<?>, AddFlowInput> receivedAddFlows = HashMultimap.create();
+ private final Multimap<InstanceIdentifier<?>, RemoveFlowInput> receivedRemoveFlows = HashMultimap.create();
+ private final Multimap<InstanceIdentifier<?>, UpdateFlowInput> receivedUpdateFlows = HashMultimap.create();
+ private RoutedRpcRegistration<SalFlowService> registration;
+
+ @Override
+ public Future<RpcResult<AddFlowOutput>> addFlow(AddFlowInput arg0) {
+ receivedAddFlows.put(arg0.getNode().getValue(), arg0);
+ return addFlowResult;
+ }
+
+ @Override
+ public Future<RpcResult<RemoveFlowOutput>> removeFlow(RemoveFlowInput arg0) {
+ receivedRemoveFlows.put(arg0.getNode().getValue(), arg0);
+ return removeFlowResult;
+ }
+
+ @Override
+ public Future<RpcResult<UpdateFlowOutput>> updateFlow(UpdateFlowInput arg0) {
+ receivedUpdateFlows.put(arg0.getNode().getValue(), arg0);
+ return updateFlowResult;
+ }
+
+ public Future<RpcResult<AddFlowOutput>> getAddFlowResult() {
+ return addFlowResult;
+ }
+
+ public MessageCapturingFlowService setAddFlowResult(Future<RpcResult<AddFlowOutput>> addFlowResult) {
+ this.addFlowResult = addFlowResult;
+ return this;
+ }
+
+ public Future<RpcResult<RemoveFlowOutput>> getRemoveFlowResult() {
+ return removeFlowResult;
+ }
+
+ public MessageCapturingFlowService setRemoveFlowResult(Future<RpcResult<RemoveFlowOutput>> removeFlowResult) {
+ this.removeFlowResult = removeFlowResult;
+ return this;
+ }
+
+ public Future<RpcResult<UpdateFlowOutput>> getUpdateFlowResult() {
+ return updateFlowResult;
+ }
+
+ public MessageCapturingFlowService setUpdateFlowResult(Future<RpcResult<UpdateFlowOutput>> updateFlowResult) {
+ this.updateFlowResult = updateFlowResult;
+ return this;
+ }
+
+ public Multimap<InstanceIdentifier<?>, AddFlowInput> getReceivedAddFlows() {
+ return receivedAddFlows;
+ }
+
+ public Multimap<InstanceIdentifier<?>, RemoveFlowInput> getReceivedRemoveFlows() {
+ return receivedRemoveFlows;
+ }
+
+ public Multimap<InstanceIdentifier<?>, UpdateFlowInput> getReceivedUpdateFlows() {
+ return receivedUpdateFlows;
+ }
+
+ public MessageCapturingFlowService registerTo(RpcProviderRegistry registry) {
+ registration = registry.addRoutedRpcImplementation(SalFlowService.class, this);
+ assertNotNull(registration);
+ return this;
+ }
+
+ public void close() throws Exception {
+ registration.close();
+ }
+
+ public MessageCapturingFlowService registerPath(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
+ registration.registerPath(context, path);
+ return this;
+ }
+
+ public MessageCapturingFlowService unregisterPath(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
+ registration.unregisterPath(context, path);
+ return this;
+ }
+
+ public static MessageCapturingFlowService create() {
+ return new MessageCapturingFlowService();
+ }
+
+ public static MessageCapturingFlowService create(RpcProviderRegistry registry) {
+ MessageCapturingFlowService ret = new MessageCapturingFlowService();
+ ret.registerTo(registry);
+ return ret;
+ }
+
+
+}
\ No newline at end of file
public interface RouteChangePublisher<C,P> {
- ListenerRegistration<RouteChangeListener<C,P>> registerRouteChangeListener(RouteChangeListener<C,P> listener);
+ <L extends RouteChangeListener<C,P>> ListenerRegistration<L> registerRouteChangeListener(L listener);
}
--- /dev/null
+package org.opendaylight.controller.md.sal.common.impl.routing;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class RoutingUtils {
+
+ public static <C,P> RouteChange<C,P> removalChange(C context,P path) {
+ final ImmutableMap<C, Set<P>> announcements = ImmutableMap.<C,Set<P>>of();
+ final ImmutableMap<C, Set<P>> removals = ImmutableMap.<C,Set<P>>of(context, ImmutableSet.of(path));
+ return new RouteChangeImpl<C,P>(announcements, removals);
+ }
+
+ public static <C,P> RouteChange<C,P> announcementChange(C context,P path) {
+ final ImmutableMap<C, Set<P>> announcements = ImmutableMap.<C,Set<P>>of(context, ImmutableSet.of(path));
+ final ImmutableMap<C, Set<P>> removals = ImmutableMap.<C,Set<P>>of();
+ return new RouteChangeImpl<C,P>(announcements, removals);
+ }
+
+
+ public static <C,P> RouteChange<C,P> change(Map<C, Set<P>> announcements,
+ Map<C, Set<P>> removals) {
+ final ImmutableMap<C, Set<P>> immutableAnnouncements = ImmutableMap.<C,Set<P>>copyOf(announcements);
+ final ImmutableMap<C, Set<P>> immutableRemovals = ImmutableMap.<C,Set<P>>copyOf(removals);
+ return new RouteChangeImpl<C,P>(immutableAnnouncements, immutableRemovals);
+ }
+
+
+ private static class RouteChangeImpl<C,P> implements RouteChange<C, P> {
+ private final Map<C, Set<P>> removal;
+ private final Map<C, Set<P>> announcement;
+
+ public RouteChangeImpl(ImmutableMap<C, Set<P>> removal, ImmutableMap<C, Set<P>> announcement) {
+ super();
+ this.removal = removal;
+ this.announcement = announcement;
+ }
+
+ @Override
+ public Map<C, Set<P>> getAnnouncements() {
+ return announcement;
+ }
+
+ @Override
+ public Map<C, Set<P>> getRemovals() {
+ return removal;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((announcement == null) ? 0 : announcement.hashCode());
+ result = prime * result + ((removal == null) ? 0 : removal.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ RouteChangeImpl other = (RouteChangeImpl) obj;
+ if (announcement == null) {
+ if (other.announcement != null)
+ return false;
+ } else if (!announcement.equals(other.announcement))
+ return false;
+ if (removal == null) {
+ if (other.removal != null) {
+ return false;
+ }
+ } else if (!removal.equals(other.removal))
+ return false;
+ return true;
+ }
+ }
+
+
+
+}
<artifactId>concepts</artifactId>
<version>0.1.1-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
</dependencies>
<packaging>bundle</packaging>
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+
+import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import com.google.common.collect.ImmutableList;
+
public class Rpcs {
+
+ public static <T> RpcResult<T> getRpcResult(boolean successful) {
+ RpcResult<T> ret = new RpcResultTO<T>(successful, null, ImmutableList.<RpcError>of());
+ return ret;
+ }
+
public static <T> RpcResult<T> getRpcResult(boolean successful, T result,
Collection<RpcError> errors) {
RpcResult<T> ret = new RpcResultTO<T>(successful, result, errors);
return ret;
}
- private static class RpcResultTO<T> implements RpcResult<T>, Serializable {
+ public static <T> RpcResult<T> getRpcResult(boolean successful, Collection<RpcError> errors) {
+ return new RpcResultTO<T>(successful, null, errors);
+ }
+
+ private static class RpcResultTO<T> implements RpcResult<T>, Serializable, Immutable {
private final Collection<RpcError> errors;
private final T result;
Collection<RpcError> errors) {
this.successful = successful;
this.result = result;
- this.errors = Collections.unmodifiableList(new ArrayList<RpcError>(
- errors));
+ this.errors = ImmutableList.copyOf(errors);
}
@Override
import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter
import org.opendaylight.yangtools.concepts.ListenerRegistration
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry
+import org.opendaylight.controller.sal.core.api.RpcImplementation
-public class BrokerImpl implements Broker, AutoCloseable {
+public class BrokerImpl implements Broker, RpcProvisionRegistry, AutoCloseable {
private static val log = LoggerFactory.getLogger(BrokerImpl);
// Broker Generic Context
deactivator?.close();
}
+ override addRpcImplementation(QName rpcType, RpcImplementation implementation) throws IllegalArgumentException {
+ router.addRpcImplementation(rpcType,implementation);
+ }
+
+ override addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+ router.addRoutedRpcImplementation(rpcType,implementation);
+ }
+
}
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
import org.slf4j.LoggerFactory
import org.opendaylight.yangtools.concepts.util.ListenerRegistry
+import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
class RpcRouterImpl implements RpcRouter, Identifiable<String> {
}
override addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+ checkNotNull(rpcType, "Rpc Type should not be null");
+ checkNotNull(implementation, "Implementation should not be null.");
+ val reg = new RoutedRpcRegistrationImpl(rpcType, implementation, this);
+ implementations.put(rpcType, reg)
+
+ for (listener : rpcRegistrationListeners.listeners) {
+ try {
+ listener.instance.onRpcImplementationAdded(rpcType);
+ } catch (Exception e) {
+ log.error("Unhandled exception during invoking listener", e);
+ }
+ }
+
+ return reg;
}
override addRpcImplementation(QName rpcType, RpcImplementation implementation) throws IllegalArgumentException {
override protected removeRegistration() {
router.remove(this);
}
+}
+class RoutedRpcRegistrationImpl extends RpcRegistrationImpl implements RoutedRpcRegistration {
+
+
+ new(QName type, RpcImplementation instance, RpcRouterImpl router) {
+ super(type,instance,router)
+ }
+ override protected removeRegistration() {
+ router.remove(this);
+ }
+ override registerPath(QName context, InstanceIdentifier path) {
+ //
+
+ }
+
+ override unregisterPath(QName context, InstanceIdentifier path) {
+ //
+ }
}
this.schema = null;
}
- protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified, boolean config) {
+ protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified,
+ boolean config) {
long startTime = System.nanoTime();
try {
- DataSchemaNode node = schemaNodeFor(path);
- return YangDataOperations.merge(node,stored,modified,config);
+ DataSchemaNode node = schemaNodeFor(path);
+ return YangDataOperations.merge(node, stored, modified, config);
} finally {
- //System.out.println("Merge time: " + ((System.nanoTime() - startTime) / 1000.0d));
+ // System.out.println("Merge time: " + ((System.nanoTime() -
+ // startTime) / 1000.0d));
}
}
-
-
+
private DataSchemaNode schemaNodeFor(InstanceIdentifier path) {
- checkState(schema != null,"YANG Schema is not available");
+ checkState(schema != null, "YANG Schema is not available");
return YangSchemaUtils.getSchemaNode(schema, path);
}
DataModification<InstanceIdentifier, CompositeNode> original) {
// NOOP for now
NormalizedDataModification normalized = new NormalizedDataModification(original);
- for (Entry<InstanceIdentifier,CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
+ for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
normalized.putConfigurationData(entry.getKey(), entry.getValue());
}
- for (Entry<InstanceIdentifier,CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
+ for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
normalized.putOperationalData(entry.getKey(), entry.getValue());
}
for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
normalized.removeConfigurationData(entry);
}
- for(InstanceIdentifier entry : original.getRemovedOperationalData()) {
+ for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
normalized.removeOperationalData(entry);
}
return normalized;
}
}
}
-
+
private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
private Object identifier;
identifier = original;
status = TransactionStatus.NEW;
}
-
+
@Override
public Object getIdentifier() {
return this.identifier;
}
-
+
@Override
public TransactionStatus getStatus() {
return status;
public Future<RpcResult<TransactionStatus>> commit() {
throw new UnsupportedOperationException("Commit should not be invoked on this");
}
-
+
@Override
- protected CompositeNode mergeConfigurationData(InstanceIdentifier path,CompositeNode stored, CompositeNode modified) {
- return mergeData(path,stored, modified,true);
+ protected CompositeNode mergeConfigurationData(InstanceIdentifier path, CompositeNode stored,
+ CompositeNode modified) {
+ return mergeData(path, stored, modified, true);
}
-
+
@Override
- protected CompositeNode mergeOperationalData(InstanceIdentifier path,CompositeNode stored, CompositeNode modified) {
+ protected CompositeNode mergeOperationalData(InstanceIdentifier path, CompositeNode stored,
+ CompositeNode modified) {
// TODO Auto-generated method stub
- return mergeData(path,stored,modified,false);
+ return mergeData(path, stored, modified, false);
}
-
}
}
checkArgument(!iterator.hasNext(), "Path nests inside leaf node, which is not allowed.");
return currentNode;
}
+ checkState(currentNode != null, "Current node should not be null for %s",path);
}
+ checkState(previous instanceof DataSchemaNode, "Schema node for %s should be instance of DataSchemaNode. Found %s",path,previous);
return (DataSchemaNode) previous;
}
-
private static DataSchemaNode searchInChoices(DataNodeContainer node, QName arg) {
Set<DataSchemaNode> children = node.getChildNodes();
--- /dev/null
+/*
+ * Copyright IBM Corporation, 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.statistics.manager;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103.TransactionId;
+
+/**
+ * Main responsibility of the class is to manage multipart response
+ * for multipart request. It also handles the flow aggregate request
+ * and response mapping.
+ * @author avishnoi@in.ibm.com
+ *
+ */
+public class MultipartMessageManager {
+
+ private static Map<TransactionId,Short> txIdTotableIdMap = new ConcurrentHashMap<TransactionId,Short>();
+
+ public MultipartMessageManager(){}
+
+ public Short getTableIdForTxId(TransactionId id){
+
+ return txIdTotableIdMap.get(id);
+
+ }
+
+ public void setTxIdAndTableIdMapEntry(TransactionId id,Short tableId){
+ txIdTotableIdMap.put(id, tableId);
+ }
+}
*/
package org.opendaylight.controller.md.statistics.manager;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.aggregate.flow.statistics.AggregateFlowStatistics;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.features.GroupFeatures;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.desc.stats.reply.GroupDescStats;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.statistics.reply.GroupStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterFeatures;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.config.stats.reply.MeterConfigStats;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericTableStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.NodeConnectorStatistics;
public class NodeStatistics {
private MeterFeatures meterFeatures;
+ private final Map<Short,Map<Flow,GenericStatistics>> flowAndStatsMap=
+ new HashMap<Short,Map<Flow,GenericStatistics>>();
+
+ private final Map<Short,AggregateFlowStatistics> tableAndAggregateFlowStatsMap =
+ new HashMap<Short,AggregateFlowStatistics>();
+
+ private final Map<NodeConnectorId,NodeConnectorStatistics> nodeConnectorStats =
+ new ConcurrentHashMap<NodeConnectorId,NodeConnectorStatistics>();
+
+ private final Map<Short,GenericTableStatistics> flowTableAndStatisticsMap =
+ new HashMap<Short,GenericTableStatistics>();
+
public NodeStatistics(){
}
public void setMeterFeatures(MeterFeatures meterFeatures) {
this.meterFeatures = meterFeatures;
}
-
+
+ public Map<Short,Map<Flow,GenericStatistics>> getFlowAndStatsMap() {
+ return flowAndStatsMap;
+ }
+
+ public Map<Short, GenericTableStatistics> getFlowTableAndStatisticsMap() {
+ return flowTableAndStatisticsMap;
+ }
+
+ public Map<Short, AggregateFlowStatistics> getTableAndAggregateFlowStatsMap() {
+ return tableAndAggregateFlowStatsMap;
+ }
+ public Map<NodeConnectorId, NodeConnectorStatistics> getNodeConnectorStats() {
+ return nodeConnectorStats;
+ }
}
*/
package org.opendaylight.controller.md.statistics.manager;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
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.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.GetFlowTablesStatisticsInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.GetFlowTablesStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GetAllGroupStatisticsInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GetAllGroupStatisticsOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GetGroupDescriptionInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.GetAllMeterConfigStatisticsInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.GetAllMeterConfigStatisticsOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.GetAllMeterStatisticsInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.GetAllMeterStatisticsOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.OpendaylightMeterStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetAllPortsStatisticsInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetAllPortsStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsService;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.NotificationListener;
private OpendaylightMeterStatisticsService meterStatsService;
+ private OpendaylightFlowStatisticsService flowStatsService;
+
+ private OpendaylightPortStatisticsService portStatsService;
+
+ private OpendaylightFlowTableStatisticsService flowTableStatsService;
+
+ private final MultipartMessageManager multipartMessageManager = new MultipartMessageManager();
+
private Thread statisticsRequesterThread;
private final InstanceIdentifier<Nodes> nodesIdentifier = InstanceIdentifier.builder(Nodes.class).toInstance();
this.nps = notificationService;
}
+ public MultipartMessageManager getMultipartMessageManager() {
+ return multipartMessageManager;
+ }
+
private final StatisticsUpdateCommiter updateCommiter = new StatisticsUpdateCommiter(StatisticsProvider.this);
private Registration<NotificationListener> listenerRegistration;
meterStatsService = StatisticsManagerActivator.getProviderContext().
getRpcService(OpendaylightMeterStatisticsService.class);
+
+ flowStatsService = StatisticsManagerActivator.getProviderContext().
+ getRpcService(OpendaylightFlowStatisticsService.class);
+
+ portStatsService = StatisticsManagerActivator.getProviderContext().
+ getRpcService(OpendaylightPortStatisticsService.class);
+ flowTableStatsService = StatisticsManagerActivator.getProviderContext().
+ getRpcService(OpendaylightFlowTableStatisticsService.class);
+
statisticsRequesterThread = new Thread( new Runnable(){
@Override
private void statsRequestSender(){
- //Need to call API to receive all the nodes connected to controller.
List<Node> targetNodes = getAllConnectedNodes();
if(targetNodes == null)
return;
+
for (Node targetNode : targetNodes){
+
+ InstanceIdentifier<Node> targetInstanceId = InstanceIdentifier.builder(Nodes.class).child(Node.class,targetNode.getKey()).toInstance();
+ NodeRef targetNodeRef = new NodeRef(targetInstanceId);
+
+ try {
+
+ sendAggregateFlowsStatsFromAllTablesRequest(targetNode.getKey());
+
+ sendAllFlowsStatsFromAllTablesRequest(targetNodeRef);
+
+ sendAllPortStatisticsRequest(targetNodeRef);
+
+ sendAllFlowTablesStatisticsRequest(targetNodeRef);
+
+ }catch(Exception e){
+ spLogger.error("Exception occured while sending statistics requests : {}",e);
+ }
if(targetNode.getAugmentation(FlowCapableNode.class) != null){
spLogger.info("Send request for stats collection to node : {})",targetNode.getId());
-
- InstanceIdentifier<Node> targetInstanceId = InstanceIdentifier.builder(Nodes.class).child(Node.class,targetNode.getKey()).toInstance();
- NodeRef targetNodeRef = new NodeRef(targetInstanceId);
-
+
try{
sendAllGroupStatisticsRequest(targetNodeRef);
Thread.sleep(1000);
sendMeterConfigStatisticsRequest(targetNodeRef);
Thread.sleep(1000);
}catch(Exception e){
- spLogger.error("Exception occured while sending statistics request : {}", e);
+ spLogger.error("Exception occured while sending statistics requests : {}", e);
}
}
}
}
+
+ private void sendAllFlowTablesStatisticsRequest(NodeRef targetNodeRef) {
+ final GetFlowTablesStatisticsInputBuilder input =
+ new GetFlowTablesStatisticsInputBuilder();
+
+ input.setNode(targetNodeRef);
+
+ @SuppressWarnings("unused")
+ Future<RpcResult<GetFlowTablesStatisticsOutput>> response =
+ flowTableStatsService.getFlowTablesStatistics(input.build());
+ }
+
+ private void sendAllFlowsStatsFromAllTablesRequest(NodeRef targetNode){
+ final GetAllFlowsStatisticsFromAllFlowTablesInputBuilder input =
+ new GetAllFlowsStatisticsFromAllFlowTablesInputBuilder();
+
+ input.setNode(targetNode);
+
+ @SuppressWarnings("unused")
+ Future<RpcResult<GetAllFlowsStatisticsFromAllFlowTablesOutput>> response =
+ flowStatsService.getAllFlowsStatisticsFromAllFlowTables(input.build());
+
+ }
+ private void sendAggregateFlowsStatsFromAllTablesRequest(NodeKey targetNodeKey) throws InterruptedException, ExecutionException{
+
+ List<Short> tablesId = getTablesFromNode(targetNodeKey);
+
+ if(tablesId.size() != 0){
+ for(Short id : tablesId){
+
+ spLogger.info("Send aggregate stats request for flow table {} to node {}",id,targetNodeKey);
+ GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder input =
+ new GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder();
+
+ input.setNode(new NodeRef(InstanceIdentifier.builder(Nodes.class).child(Node.class, targetNodeKey).toInstance()));
+ input.setTableId(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId(id));
+ Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput>> response =
+ flowStatsService.getAggregateFlowStatisticsFromFlowTableForAllFlows(input.build());
+
+ multipartMessageManager.setTxIdAndTableIdMapEntry(response.get().getResult().getTransactionId(), id);
+ }
+ }
+
+ //Note: Just for testing, because i am not able to fetch table list from datastore
+ // Bug-225 is raised for investigation.
+
+// spLogger.info("Send aggregate stats request for flow table {} to node {}",1,targetNodeKey);
+// GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder input =
+// new GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder();
+//
+// input.setNode(new NodeRef(InstanceIdentifier.builder(Nodes.class).child(Node.class, targetNodeKey).toInstance()));
+// input.setTableId(new TableId((short)1));
+// Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput>> response =
+// flowStatsService.getAggregateFlowStatisticsFromFlowTableForAllFlows(input.build());`
+//
+// multipartMessageManager.setTxIdAndTableIdMapEntry(response.get().getResult().getTransactionId(), (short)1);
+ }
+
+ private void sendAllPortStatisticsRequest(NodeRef targetNode){
+
+ final GetAllPortsStatisticsInputBuilder input = new GetAllPortsStatisticsInputBuilder();
+
+ input.setNode(targetNode);
+
+ @SuppressWarnings("unused")
+ Future<RpcResult<GetAllPortsStatisticsOutput>> response =
+ portStatsService.getAllPortsStatistics(input.build());
+ }
+
private void sendAllGroupStatisticsRequest(NodeRef targetNode){
final GetAllGroupStatisticsInputBuilder input = new GetAllGroupStatisticsInputBuilder();
- input.setNode(targetNode);
input.setNode(targetNode);
@SuppressWarnings("unused")
spLogger.info("Number of connected nodes : {}",nodes.getNode().size());
return nodes.getNode();
}
+
+ private List<Short> getTablesFromNode(NodeKey nodeKey){
+ InstanceIdentifier<FlowCapableNode> nodesIdentifier = InstanceIdentifier.builder(Nodes.class).child(Node.class,nodeKey).augmentation(FlowCapableNode.class).toInstance();
+
+ FlowCapableNode node = (FlowCapableNode)dps.readConfigurationData(nodesIdentifier);
+ List<Short> tablesId = new ArrayList<Short>();
+ if(node != null && node.getTable()!=null){
+ spLogger.info("Number of tables {} supported by node {}",node.getTable().size(),nodeKey);
+ for(Table table: node.getTable()){
+ tablesId.add(table.getId());
+ }
+ }
+ return tablesId;
+ }
@SuppressWarnings("deprecation")
@Override
*/
package org.opendaylight.controller.md.statistics.manager;
+import java.util.HashMap;
+import java.util.List;
import java.util.concurrent.ConcurrentMap;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+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.TableBuilder;
+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.statistics.rev130819.AggregateFlowStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowTableStatisticsUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.NodeConnectorStatisticsUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.aggregate.flow.statistics.AggregateFlowStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.and.statistics.map.FlowTableAndStatisticsMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatisticsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupDescStatsUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupFeaturesUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupStatisticsUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
+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.NodeBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterConfigStatsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterFeaturesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.PortStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.flow.capable.node.connector.statistics.FlowCapableNodeConnectorStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMap;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class StatisticsUpdateCommiter implements OpendaylightGroupStatisticsListener,
- OpendaylightMeterStatisticsListener {
+ OpendaylightMeterStatisticsListener,
+ OpendaylightFlowStatisticsListener,
+ OpendaylightPortStatisticsListener,
+ OpendaylightFlowTableStatisticsListener{
+ public final static Logger sucLogger = LoggerFactory.getLogger(StatisticsUpdateCommiter.class);
+
private final StatisticsProvider statisticsManager;
+
+ private final int unaccountedFlowsCounter = 1;
public StatisticsUpdateCommiter(final StatisticsProvider manager){
it.commit();
}
+ @Override
+ public void onFlowsStatisticsUpdate(FlowsStatisticsUpdate notification) {
+ NodeKey key = new NodeKey(notification.getId());
+ sucLogger.info("Received flow stats update : {}",notification.toString());
+
+ for(FlowAndStatisticsMapList map: notification.getFlowAndStatisticsMapList()){
+ short tableId = map.getTableId();
+
+ DataModificationTransaction it = this.statisticsManager.startChange();
+
+ boolean foundOriginalFlow = false;
+
+ FlowBuilder flowBuilder = new FlowBuilder();
+
+ FlowStatisticsDataBuilder flowStatisticsData = new FlowStatisticsDataBuilder();
+
+ FlowBuilder flow = new FlowBuilder();
+ flow.setContainerName(map.getContainerName());
+ flow.setBufferId(map.getBufferId());
+ flow.setCookie(map.getCookie());
+ flow.setCookieMask(map.getCookieMask());
+ flow.setFlags(map.getFlags());
+ flow.setFlowName(map.getFlowName());
+ flow.setHardTimeout(map.getHardTimeout());
+ if(map.getFlowId() != null)
+ flow.setId(new FlowId(map.getFlowId().getValue()));
+ flow.setIdleTimeout(map.getIdleTimeout());
+ flow.setInstallHw(map.isInstallHw());
+ flow.setInstructions(map.getInstructions());
+ if(map.getFlowId()!= null)
+ flow.setKey(new FlowKey(new FlowId(map.getKey().getFlowId().getValue())));
+ flow.setMatch(map.getMatch());
+ flow.setOutGroup(map.getOutGroup());
+ flow.setOutPort(map.getOutPort());
+ flow.setPriority(map.getPriority());
+ flow.setStrict(map.isStrict());
+ flow.setTableId(tableId);
+
+ Flow flowRule = flow.build();
+
+ FlowAndStatisticsMapListBuilder stats = new FlowAndStatisticsMapListBuilder();
+ stats.setByteCount(map.getByteCount());
+ stats.setPacketCount(map.getPacketCount());
+ stats.setDuration(map.getDuration());
+
+ GenericStatistics flowStats = stats.build();
+
+ //Add statistics to local cache
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
+ }
+ if(!cache.get(notification.getId()).getFlowAndStatsMap().containsKey(tableId)){
+ cache.get(notification.getId()).getFlowAndStatsMap().put(tableId, new HashMap<Flow,GenericStatistics>());
+ }
+ cache.get(notification.getId()).getFlowAndStatsMap().get(tableId).put(flowRule,flowStats);
+
+ //Augment the data to the flow node
+
+ FlowStatisticsBuilder flowStatistics = new FlowStatisticsBuilder();
+ flowStatistics.setByteCount(flowStats.getByteCount());
+ flowStatistics.setPacketCount(flowStats.getPacketCount());
+ flowStatistics.setDuration(flowStats.getDuration());
+ flowStatistics.setContainerName(map.getContainerName());
+ flowStatistics.setBufferId(map.getBufferId());
+ flowStatistics.setCookie(map.getCookie());
+ flowStatistics.setCookieMask(map.getCookieMask());
+ flowStatistics.setFlags(map.getFlags());
+ flowStatistics.setFlowName(map.getFlowName());
+ flowStatistics.setHardTimeout(map.getHardTimeout());
+ flowStatistics.setIdleTimeout(map.getIdleTimeout());
+ flowStatistics.setInstallHw(map.isInstallHw());
+ flowStatistics.setInstructions(map.getInstructions());
+ flowStatistics.setMatch(map.getMatch());
+ flowStatistics.setOutGroup(map.getOutGroup());
+ flowStatistics.setOutPort(map.getOutPort());
+ flowStatistics.setPriority(map.getPriority());
+ flowStatistics.setStrict(map.isStrict());
+ flowStatistics.setTableId(tableId);
+
+ flowStatisticsData.setFlowStatistics(flowStatistics.build());
+
+ sucLogger.info("Flow : {}",flowRule.toString());
+ sucLogger.info("Statistics to augment : {}",flowStatistics.build().toString());
+
+ InstanceIdentifier<Table> tableRef = InstanceIdentifier.builder(Nodes.class).child(Node.class, key)
+ .augmentation(FlowCapableNode.class).child(Table.class, new TableKey(tableId)).toInstance();
+
+ Table table= (Table)it.readConfigurationData(tableRef);
+
+ //TODO: Not a good way to do it, need to figure out better way.
+ //TODO: major issue in any alternate approach is that flow key is incrementally assigned
+ //to the flows stored in data store.
+ if(table != null){
+
+ for(Flow existingFlow : table.getFlow()){
+ sucLogger.debug("Existing flow in data store : {}",existingFlow.toString());
+ if(flowEquals(flowRule,existingFlow)){
+ InstanceIdentifier<Flow> flowRef = InstanceIdentifier.builder(Nodes.class).child(Node.class, key)
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, new TableKey(tableId))
+ .child(Flow.class,existingFlow.getKey()).toInstance();
+ flowBuilder.setKey(existingFlow.getKey());
+ flowBuilder.addAugmentation(FlowStatisticsData.class, flowStatisticsData.build());
+ sucLogger.debug("Found matching flow in the datastore, augmenting statistics");
+ foundOriginalFlow = true;
+ it.putOperationalData(flowRef, flowBuilder.build());
+ it.commit();
+ break;
+ }
+ }
+ }
+
+ if(!foundOriginalFlow){
+ sucLogger.info("Associated original flow is not found in data store. Augmenting flow in operational data st");
+ //TODO: Temporary fix: format [ 0+tableid+0+unaccounted flow counter]
+ long flowKey = Long.getLong(new String("0"+Short.toString(tableId)+"0"+Integer.toString(this.unaccountedFlowsCounter)));
+ FlowKey newFlowKey = new FlowKey(new FlowId(flowKey));
+ InstanceIdentifier<Flow> flowRef = InstanceIdentifier.builder(Nodes.class).child(Node.class, key)
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, new TableKey(tableId))
+ .child(Flow.class,newFlowKey).toInstance();
+ flowBuilder.setKey(newFlowKey);
+ flowBuilder.addAugmentation(FlowStatisticsData.class, flowStatisticsData.build());
+ sucLogger.debug("Flow was no present in data store, augmenting statistics as an unaccounted flow");
+ it.putOperationalData(flowRef, flowBuilder.build());
+ it.commit();
+ }
+ }
+ }
+
+ @Override
+ public void onAggregateFlowStatisticsUpdate(AggregateFlowStatisticsUpdate notification) {
+ NodeKey key = new NodeKey(notification.getId());
+ sucLogger.debug("Received aggregate flow statistics update : {}",notification.toString());
+
+ Short tableId = this.statisticsManager.getMultipartMessageManager().getTableIdForTxId(notification.getTransactionId());
+ if(tableId != null){
+
+ DataModificationTransaction it = this.statisticsManager.startChange();
+
+ InstanceIdentifier<Table> tableRef = InstanceIdentifier.builder(Nodes.class).child(Node.class, key)
+ .augmentation(FlowCapableNode.class).child(Table.class, new TableKey(tableId)).toInstance();
+
+ AggregateFlowStatisticsDataBuilder aggregateFlowStatisticsDataBuilder = new AggregateFlowStatisticsDataBuilder();
+ AggregateFlowStatisticsBuilder aggregateFlowStatisticsBuilder = new AggregateFlowStatisticsBuilder();
+ aggregateFlowStatisticsBuilder.setByteCount(notification.getByteCount());
+ aggregateFlowStatisticsBuilder.setFlowCount(notification.getFlowCount());
+ aggregateFlowStatisticsBuilder.setPacketCount(notification.getPacketCount());
+ aggregateFlowStatisticsDataBuilder.setAggregateFlowStatistics(aggregateFlowStatisticsBuilder.build());
+
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
+ }
+ cache.get(notification.getId()).getTableAndAggregateFlowStatsMap().put(tableId,aggregateFlowStatisticsBuilder.build());
+
+ sucLogger.debug("Augment aggregate statistics: {} for table {} on Node {}",aggregateFlowStatisticsBuilder.build().toString(),tableId,key);
+
+ TableBuilder tableBuilder = new TableBuilder();
+ tableBuilder.setKey(new TableKey(tableId));
+ tableBuilder.addAugmentation(AggregateFlowStatisticsData.class, aggregateFlowStatisticsDataBuilder.build());
+ it.putOperationalData(tableRef, tableBuilder.build());
+ it.commit();
+
+ }
+ }
+
+ @Override
+ public void onPortStatisticsUpdate(PortStatisticsUpdate notification) {
+ NodeKey key = new NodeKey(notification.getId());
+ sucLogger.info("Received port stats update : {}",notification.toString());
+
+ //Add statistics to local cache
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
+ }
+
+
+ List<NodeConnectorStatisticsAndPortNumberMap> portsStats = notification.getNodeConnectorStatisticsAndPortNumberMap();
+ for(NodeConnectorStatisticsAndPortNumberMap portStats : portsStats){
+
+ DataModificationTransaction it = this.statisticsManager.startChange();
+
+ FlowCapableNodeConnectorStatisticsBuilder statisticsBuilder
+ = new FlowCapableNodeConnectorStatisticsBuilder();
+ statisticsBuilder.setBytes(portStats.getBytes());
+ statisticsBuilder.setCollisionCount(portStats.getCollisionCount());
+ statisticsBuilder.setDuration(portStats.getDuration());
+ statisticsBuilder.setPackets(portStats.getPackets());
+ statisticsBuilder.setReceiveCrcError(portStats.getReceiveCrcError());
+ statisticsBuilder.setReceiveDrops(portStats.getReceiveDrops());
+ statisticsBuilder.setReceiveErrors(portStats.getReceiveErrors());
+ statisticsBuilder.setReceiveFrameError(portStats.getReceiveFrameError());
+ statisticsBuilder.setReceiveOverRunError(portStats.getReceiveOverRunError());
+ statisticsBuilder.setTransmitDrops(portStats.getTransmitDrops());
+ statisticsBuilder.setTransmitErrors(portStats.getTransmitErrors());
+
+ //Update data in the cache
+ cache.get(notification.getId()).getNodeConnectorStats().put(portStats.getNodeConnectorId(), statisticsBuilder.build());
+
+ //Augment data to the node-connector
+ FlowCapableNodeConnectorStatisticsDataBuilder statisticsDataBuilder =
+ new FlowCapableNodeConnectorStatisticsDataBuilder();
+
+ statisticsDataBuilder.setFlowCapableNodeConnectorStatistics(statisticsBuilder.build());
+
+ InstanceIdentifier<NodeConnector> nodeConnectorRef = InstanceIdentifier.builder(Nodes.class).child(Node.class, key).child(NodeConnector.class, new NodeConnectorKey(portStats.getNodeConnectorId())).toInstance();
+
+ NodeConnector nodeConnector = (NodeConnector)it.readOperationalData(nodeConnectorRef);
+
+ if(nodeConnector != null){
+ sucLogger.debug("Augmenting port statistics {} to port {}",statisticsDataBuilder.build().toString(),nodeConnectorRef.toString());
+ NodeConnectorBuilder nodeConnectorBuilder = new NodeConnectorBuilder();
+ nodeConnectorBuilder.addAugmentation(FlowCapableNodeConnectorStatisticsData.class, statisticsDataBuilder.build());
+ it.putOperationalData(nodeConnectorRef, nodeConnectorBuilder.build());
+ it.commit();
+ }
+ }
+ }
+
+ @Override
+ public void onFlowTableStatisticsUpdate(FlowTableStatisticsUpdate notification) {
+ NodeKey key = new NodeKey(notification.getId());
+ sucLogger.debug("Received flow table statistics update : {}",notification.toString());
+
+ List<FlowTableAndStatisticsMap> flowTablesStatsList = notification.getFlowTableAndStatisticsMap();
+ for (FlowTableAndStatisticsMap ftStats : flowTablesStatsList){
+
+ DataModificationTransaction it = this.statisticsManager.startChange();
+
+ InstanceIdentifier<Table> tableRef = InstanceIdentifier.builder(Nodes.class).child(Node.class, key)
+ .augmentation(FlowCapableNode.class).child(Table.class, new TableKey(ftStats.getTableId().getValue())).toInstance();
+
+ FlowTableStatisticsDataBuilder statisticsDataBuilder = new FlowTableStatisticsDataBuilder();
+
+ FlowTableStatisticsBuilder statisticsBuilder = new FlowTableStatisticsBuilder();
+ statisticsBuilder.setActiveFlows(ftStats.getActiveFlows());
+ statisticsBuilder.setPacketsLookedUp(ftStats.getPacketsLookedUp());
+ statisticsBuilder.setPacketsMatched(ftStats.getPacketsMatched());
+
+ statisticsDataBuilder.setFlowTableStatistics(statisticsBuilder.build());
+
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
+ }
+ cache.get(notification.getId()).getFlowTableAndStatisticsMap().put(ftStats.getTableId().getValue(),statisticsBuilder.build());
+
+ sucLogger.debug("Augment flow table statistics: {} for table {} on Node {}",statisticsBuilder.build().toString(),ftStats.getTableId(),key);
+
+ TableBuilder tableBuilder = new TableBuilder();
+ tableBuilder.setKey(new TableKey(ftStats.getTableId().getValue()));
+ tableBuilder.addAugmentation(FlowTableStatisticsData.class, statisticsDataBuilder.build());
+ it.putOperationalData(tableRef, tableBuilder.build());
+ it.commit();
+ }
+ }
+
+ @Override
+ public void onFlowStatisticsUpdated(FlowStatisticsUpdated notification) {
+ // TODO Auto-generated method stub
+ //TODO: Depricated, will clean it up once sal-compatibility is fixed.
+ //Sal-Compatibility code usage this notification event.
+
+ }
+
+ @Override
+ public void onFlowTableStatisticsUpdated(FlowTableStatisticsUpdated notification) {
+ // TODO Auto-generated method stub
+ //TODO: Need to implement it yet
+
+ }
+
+ @Override
+ public void onNodeConnectorStatisticsUpdated(NodeConnectorStatisticsUpdated notification) {
+ // TODO Auto-generated method stub
+ //TODO: Need to implement it yet
+
+ }
+
private NodeRef getNodeRef(NodeKey nodeKey){
InstanceIdentifierBuilder<?> builder = InstanceIdentifier.builder(Nodes.class).child(Node.class, nodeKey);
return new NodeRef(builder.toInstance());
}
-
+
+ public boolean flowEquals(Flow statsFlow, Flow storedFlow) {
+ if (statsFlow.getClass() != storedFlow.getClass()) {
+ return false;
+ }
+ if (statsFlow.getBufferId()== null) {
+ if (storedFlow.getBufferId() != null) {
+ return false;
+ }
+ } else if(!statsFlow.getBufferId().equals(storedFlow.getBufferId())) {
+ return false;
+ }
+ if (statsFlow.getContainerName()== null) {
+ if (storedFlow.getContainerName()!= null) {
+ return false;
+ }
+ } else if(!statsFlow.getContainerName().equals(storedFlow.getContainerName())) {
+ return false;
+ }
+ if (statsFlow.getCookie()== null) {
+ if (storedFlow.getCookie()!= null) {
+ return false;
+ }
+ } else if(!statsFlow.getCookie().equals(storedFlow.getCookie())) {
+ return false;
+ }
+ if (statsFlow.getMatch()== null) {
+ if (storedFlow.getMatch() != null) {
+ return false;
+ }
+ } else if(!statsFlow.getMatch().equals(storedFlow.getMatch())) {
+ return false;
+ }
+ if (statsFlow.getCookie()== null) {
+ if (storedFlow.getCookie()!= null) {
+ return false;
+ }
+ } else if(!statsFlow.getCookie().equals(storedFlow.getCookie())) {
+ return false;
+ }
+ if (statsFlow.getHardTimeout() == null) {
+ if (storedFlow.getHardTimeout() != null) {
+ return false;
+ }
+ } else if(!statsFlow.getHardTimeout().equals(storedFlow.getHardTimeout() )) {
+ return false;
+ }
+ if (statsFlow.getIdleTimeout()== null) {
+ if (storedFlow.getIdleTimeout() != null) {
+ return false;
+ }
+ } else if(!statsFlow.getIdleTimeout().equals(storedFlow.getIdleTimeout())) {
+ return false;
+ }
+ if (statsFlow.getPriority() == null) {
+ if (storedFlow.getPriority() != null) {
+ return false;
+ }
+ } else if(!statsFlow.getPriority().equals(storedFlow.getPriority())) {
+ return false;
+ }
+ if (statsFlow.getTableId() == null) {
+ if (storedFlow.getTableId() != null) {
+ return false;
+ }
+ } else if(!statsFlow.getTableId().equals(storedFlow.getTableId())) {
+ return false;
+ }
+ return true;
+ }
}
this.lastAttribute = attributeIfc;
+ OpenType<?> openType = attributeIfc.getOpenType();
+
if (attributeIfc instanceof JavaAttribute) {
try {
if(((JavaAttribute)attributeIfc).getTypeDefinition() instanceof BinaryTypeDefinition) {
- return caseJavaBinaryAttribute(attributeIfc.getOpenType());
+ return caseJavaBinaryAttribute(openType);
+ } else if(((JavaAttribute)attributeIfc).isUnion()) {
+ return caseJavaUnionAttribute(openType);
} else
- return caseJavaAttribute(attributeIfc.getOpenType());
+ return caseJavaAttribute(openType);
} catch (UnknownOpenTypeException e) {
throw getIllegalArgumentException(attributeIfc);
}
} else if (attributeIfc instanceof DependencyAttribute) {
return caseDependencyAttribute(((DependencyAttribute) attributeIfc).getOpenType());
} else if (attributeIfc instanceof ListAttribute) {
- return caseListAttribute((ArrayType<?>) attributeIfc.getOpenType());
+ return caseListAttribute((ArrayType<?>) openType);
} else if (attributeIfc instanceof ListDependenciesAttribute) {
- return caseListDependeciesAttribute((ArrayType<?>) attributeIfc.getOpenType());
+ return caseListDependeciesAttribute((ArrayType<?>) openType);
} else if (attributeIfc instanceof TOAttribute) {
return caseTOAttribute(((TOAttribute) attributeIfc).getOpenType());
}
throw getIllegalArgumentException(attributeIfc);
}
+ protected T caseJavaUnionAttribute(OpenType<?> openType) {
+ return caseJavaAttribute(openType);
+ }
+
protected T caseJavaBinaryAttribute(OpenType<?> openType) {
return caseJavaAttribute(openType);
}
-/**
- * @author Maros Marsalek
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
- * 12 2013
- *
- * Copyright (c) 2012 by Cisco Systems, Inc.
- * All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
return new SimpleBinaryAttributeReadingStrategy(lastAttribute.getNullableDefault());
}
+ @Override
+ protected AttributeReadingStrategy caseJavaUnionAttribute(OpenType<?> openType) {
+ String mappingKey = JavaAttribute.DESCRIPTION_OF_VALUE_ATTRIBUTE_FOR_UNION;
+ return new SimpleUnionAttributeReadingStrategy(lastAttribute.getNullableDefault(), mappingKey);
+ }
+
@Override
public AttributeReadingStrategy caseJavaSimpleAttribute(SimpleType<?> openType) {
return new SimpleAttributeReadingStrategy(lastAttribute.getNullableDefault());
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import java.util.List;
+import java.util.Map;
+
+public class SimpleUnionAttributeReadingStrategy extends SimpleAttributeReadingStrategy {
+
+ private final String key;
+
+ public SimpleUnionAttributeReadingStrategy(String nullableDefault, String key) {
+ super(nullableDefault);
+ this.key = key;
+ }
+
+ protected Object postprocessParsedValue(String textContent) {
+ char[] charArray = textContent.toCharArray();
+ List<String> chars = Lists.newArrayListWithCapacity(charArray.length);
+
+ for (char c : charArray) {
+ chars.add(Character.toString(c));
+ }
+
+ Map<String, Object> map = Maps.newHashMap();
+ map.put(key, chars);
+ return map;
+ }
+
+ @Override
+ protected Object postprocessNullableDefault(String nullableDefault) {
+ return nullableDefault == null ? null : postprocessParsedValue(nullableDefault);
+ }
+}
Map<String, Object> retVal = Maps.newHashMap();
for (String jmxName : jmxToJavaNameMapping.keySet()) {
- String innerAttrJmxName = jmxName;
- Object innerValue = compositeData.get(innerAttrJmxName);
-
- AttributeMappingStrategy<?, ? extends OpenType<?>> attributeMappingStrategy = innerStrategies
- .get(innerAttrJmxName);
- Optional<?> mapAttribute = attributeMappingStrategy.mapAttribute(innerValue);
- if (mapAttribute.isPresent())
- retVal.put(jmxToJavaNameMapping.get(innerAttrJmxName), mapAttribute.get());
+ Optional<?> mapped = mapInnerAttribute(compositeData, jmxName, expectedType.getDescription(jmxName));
+ if(mapped.isPresent())
+ retVal.put(jmxToJavaNameMapping.get(jmxName), mapped.get());
}
return Optional.of(retVal);
}
+ protected Optional<?> mapInnerAttribute(CompositeDataSupport compositeData, String jmxName, String description) {
+ Object innerValue = compositeData.get(jmxName);
+
+ AttributeMappingStrategy<?, ? extends OpenType<?>> attributeMappingStrategy = innerStrategies
+ .get(jmxName);
+ Optional<?> mapAttribute = attributeMappingStrategy.mapAttribute(innerValue);
+ return mapAttribute;
+ }
+
}
return new CompositeAttributeMappingStrategy(openType, innerStrategies, attributeMapping);
}
+ @Override
+ protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseJavaUnionAttribute(OpenType<?> openType) {
+ Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies = Maps.newHashMap();
+
+ Map<String, String> attributeMapping = Maps.newHashMap();
+
+ CompositeType compositeType = (CompositeType) openType;
+ for (String innerAttributeKey : compositeType.keySet()) {
+
+ innerStrategies.put(innerAttributeKey, caseJavaAttribute(compositeType.getType(innerAttributeKey)));
+ attributeMapping.put(innerAttributeKey, innerAttributeKey);
+ }
+
+ return new UnionCompositeAttributeMappingStrategy(compositeType, innerStrategies, attributeMapping);
+ }
+
private String serviceNameOfDepAttr;
private String namespaceOfDepAttr;
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import java.util.Map;
+
+public class UnionCompositeAttributeMappingStrategy extends
+ CompositeAttributeMappingStrategy {
+
+
+ public UnionCompositeAttributeMappingStrategy(CompositeType compositeType, Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies, Map<String, String> jmxToJavaNameMapping) {
+ super(compositeType, innerStrategies, jmxToJavaNameMapping);
+ }
+
+ @Override
+ protected Optional<?> mapInnerAttribute(CompositeDataSupport compositeData, String jmxName, String description) {
+ if(description.equals(JavaAttribute.DESCRIPTION_OF_VALUE_ATTRIBUTE_FOR_UNION) == false)
+ return Optional.absent();
+
+ return super.mapInnerAttribute(compositeData, jmxName, description);
+ }
+}
import javax.management.openmbean.OpenType;
import java.util.Map;
-final class CompositeAttributeResolvingStrategy extends
+class CompositeAttributeResolvingStrategy extends
AbstractAttributeResolvingStrategy<CompositeDataSupport, CompositeType> {
private final Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerTypes;
private final Map<String, String> yangToJavaAttrMapping;
Util.checkType(value, Map.class);
Map<?, ?> valueMap = (Map<?, ?>) value;
+ valueMap = preprocessValueMap(valueMap);
Map<String, Object> items = Maps.newHashMap();
Map<String, OpenType<?>> openTypes = Maps.newHashMap();
return Optional.of(parsedValue);
}
+
+ protected Map<?, ?> preprocessValueMap(Map<?, ?> valueMap) {
+ return valueMap;
+ }
}
@Override
protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseJavaCompositeAttribute(CompositeType openType) {
Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerMap = Maps.newHashMap();
-
Map<String, String> yangToJmxMapping = Maps.newHashMap();
+
+ fillMappingForComposite(openType, innerMap, yangToJmxMapping);
+ return new CompositeAttributeResolvingStrategy(innerMap, openType, yangToJmxMapping);
+ }
+
+ private void fillMappingForComposite(CompositeType openType, Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerMap, Map<String, String> yangToJmxMapping) {
for (String innerAttributeKey : openType.keySet()) {
innerMap.put(innerAttributeKey, caseJavaAttribute(openType.getType(innerAttributeKey)));
yangToJmxMapping.put(innerAttributeKey, innerAttributeKey);
}
- return new CompositeAttributeResolvingStrategy(innerMap, openType, yangToJmxMapping);
+ }
+
+ @Override
+ protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseJavaUnionAttribute(OpenType<?> openType) {
+
+ Preconditions.checkState(openType instanceof CompositeType, "Unexpected open type, expected %s but was %s");
+ CompositeType compositeType = (CompositeType) openType;
+
+ Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerMap = Maps.newHashMap();
+ Map<String, String> yangToJmxMapping = Maps.newHashMap();
+ fillMappingForComposite(compositeType, innerMap, yangToJmxMapping);
+
+ return new UnionCompositeAttributeResolvingStrategy(innerMap, compositeType, yangToJmxMapping);
}
@Override
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import java.util.Map;
+
+final class UnionCompositeAttributeResolvingStrategy extends CompositeAttributeResolvingStrategy {
+
+ UnionCompositeAttributeResolvingStrategy(Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerTypes,
+ CompositeType openType, Map<String, String> yangToJavaAttrMapping) {
+ super(innerTypes, openType, yangToJavaAttrMapping);
+ }
+
+ protected Map<String, Object> preprocessValueMap(Map<?, ?> valueMap) {
+ CompositeType openType = getOpenType();
+
+ Preconditions.checkArgument(
+ valueMap.size() == 1 && valueMap.containsKey(JavaAttribute.DESCRIPTION_OF_VALUE_ATTRIBUTE_FOR_UNION),
+ "Unexpected structure of incoming map, expecting one element under %s, but was %s",
+ JavaAttribute.DESCRIPTION_OF_VALUE_ATTRIBUTE_FOR_UNION, valueMap);
+
+ Map<String, Object> newMap = Maps.newHashMap();
+
+ for (String key : openType.keySet()) {
+ if (openType.getDescription(key).equals(JavaAttribute.DESCRIPTION_OF_VALUE_ATTRIBUTE_FOR_UNION))
+ newMap.put(key, valueMap.get(JavaAttribute.DESCRIPTION_OF_VALUE_ATTRIBUTE_FOR_UNION));
+ else
+ newMap.put(key, null);
+ }
+ return newMap;
+ }
+}
return new SimpleCompositeAttributeWritingStrategy(document, key);
}
+ @Override
+ protected AttributeWritingStrategy caseJavaUnionAttribute(OpenType<?> openType) {
+ return new SimpleUnionAttributeWritingStrategy(document, key);
+ }
+
@Override
protected AttributeWritingStrategy caseDependencyAttribute(SimpleType<?> openType) {
return new ObjectNameAttributeWritingStrategy(document, key);
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.w3c.dom.Document;
+
+import java.util.List;
+import java.util.Map;
+
+public class SimpleUnionAttributeWritingStrategy extends SimpleAttributeWritingStrategy {
+
+ /**
+ * @param document
+ * @param key
+ */
+ public SimpleUnionAttributeWritingStrategy(Document document, String key) {
+ super(document, key);
+ }
+
+ protected Object preprocess(Object value) {
+ Util.checkType(value, Map.class);
+ Preconditions.checkArgument(((Map)value).size() == 1, "Unexpected number of values in %s, expected 1", value);
+ Object listOfStrings = ((Map) value).values().iterator().next();
+ Util.checkType(listOfStrings, List.class);
+
+ StringBuilder b = new StringBuilder();
+ for (Object character: (List)listOfStrings) {
+ Util.checkType(character, String.class);
+ b.append(character);
+ }
+
+ return b.toString();
+ }
+
+}
Preconditions.checkArgument(refNameToInstance != null, "No serviceInstances mapped to " + serviceName + " , "
+ serviceNameToRefNameToInstance.keySet());
- ServiceInstance serviceInstance = ServiceInstance.fromString(refNameToInstance.get(refName));
+ String instanceId = refNameToInstance.get(refName);
+ Preconditions.checkArgument(instanceId != null, "No serviceInstances mapped to " + serviceName + ":"
+ + refName + ", " + serviceNameToRefNameToInstance.keySet());
+
+ ServiceInstance serviceInstance = ServiceInstance.fromString(instanceId);
Preconditions.checkArgument(serviceInstance != null, "No serviceInstance mapped to " + refName
+ " under service name " + serviceName + " , " + refNameToInstance.keySet());
return serviceInstance;
public static void checkType(final Object value, final Class<?> clazz) {
Preconditions.checkArgument(clazz.isAssignableFrom(value.getClass()), "Unexpected type " + value.getClass()
- + " should be " + clazz);
+ + " should be " + clazz + " of " + value);
}
// TODO: add message and proper error types
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.junit.Before;
import org.junit.Ignore;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
"ref_from_code_to_instance-from-code_1");
edit("netconfMessages/editConfig_addServiceName.xml");
- config = getConfigCandidate();
+ config = getConfigCandidate();
assertCorrectServiceNames(config, 7, "ref_test2", "user_to_instance_from_code", "ref_dep_user",
"ref_dep_user_two", "ref_from_code_to_instance-from-code_dep_1",
"ref_from_code_to_instance-from-code_1", "ref_dep_user_another");
}
}
+ @Test
+ public void testConfigNetconfUnionTypes() throws Exception {
+
+ createModule(INSTANCE_NAME);
+
+ edit("netconfMessages/editConfig.xml");
+ commit();
+ Element response = getConfigRunning();
+ String trimmedResponse = XmlUtil.toString(response).replaceAll("\\s", "");
+ assertContainsString(trimmedResponse, "<ipxmlns=\"urn:opendaylight:params:xml:ns:yang:controller:test:impl\">0:0:0:0:0:0:0:1</ip>");
+ assertContainsString(trimmedResponse, "<union-test-attrxmlns=\"urn:opendaylight:params:xml:ns:yang:controller:test:impl\">456</union-test-attr>");
+
+
+ edit("netconfMessages/editConfig_setUnions.xml");
+ commit();
+ response = getConfigRunning();
+
+ trimmedResponse = XmlUtil.toString(response).replaceAll("\\s", "");
+ assertContainsString(trimmedResponse, "<ipxmlns=\"urn:opendaylight:params:xml:ns:yang:controller:test:impl\">127.1.2.3</ip>");
+ assertContainsString(trimmedResponse, "<union-test-attrxmlns=\"urn:opendaylight:params:xml:ns:yang:controller:test:impl\">randomStringForUnion</union-test-attr>");
+
+ }
+
@Test
public void testConfigNetconf() throws Exception {
XmlElement modulesElement = XmlElement.fromDomElement(response).getOnlyChildElement("data")
.getOnlyChildElement("modules");
- XmlElement configAttributeType = null;
+ List<String> expectedValues = Lists.newArrayList("default-string", "configAttributeType");
+ Set<String> configAttributeType = Sets.newHashSet();
+
for (XmlElement moduleElement : modulesElement.getChildElements("module")) {
for (XmlElement type : moduleElement.getChildElements("type")) {
if (type.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY).equals("") == false) {
- configAttributeType = type;
+ configAttributeType.add(type.getTextContent());
}
}
}
- // TODO verify if should be default value
- assertEquals("default-string", configAttributeType.getTextContent());
+ for (String expectedValue : expectedValues) {
+ assertTrue(configAttributeType.contains(expectedValue));
+ }
}
private Map<String, Map<String, ModuleMXBeanEntry>> getMbes() throws Exception {
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
+ <artifactId>config-persister-file-xml-adapter</artifactId>
+ <scope>test</scope>
+ <version>${config.version}</version>
</dependency>
<!-- test dependencies -->
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-adapter</artifactId>
- <version>${parent.version}</version>
+ <artifactId>config-persister-directory-xml-adapter</artifactId>
+ <version>${config.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
* Example configuration:<pre>
netconf.config.persister.active=2,3
# read startup configuration
- netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.DirectoryStorageAdapter
+ netconf.config.persister.1.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
-/**
- * @author Tomas Olvecky
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
- * 11 2013
- *
- * Copyright (c) 2013 by Cisco Systems, Inc.
- * All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.controller.netconf.persist.impl;
import 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.storage.file.FileStorageAdapter;
+import org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter;
import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
import org.opendaylight.controller.netconf.persist.impl.osgi.PropertiesProviderBaseImpl;
List<PersisterWithConfiguration> persisters = persisterAggregator.getPersisterWithConfigurations();
assertEquals(1, persisters.size());
PersisterWithConfiguration persister = persisters.get(0);
- assertEquals(FileStorageAdapter.class.getName() ,persister.getStorage().getClass().getName());
+ assertEquals(XmlFileStorageAdapter.class.getName() ,persister.getStorage().getClass().getName());
assertFalse(persister.isReadOnly());
}
netconf.config.persister.active=2
# read startup configuration
-netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.DirectoryStorageAdapter
+netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
netconf.config.persister.1.properties.directoryStorage=target/configuration/initial/
netconf.config.persister.1.readonly=true
-netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
+netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
netconf.config.persister.2.properties.fileStorage=target/configuration/current/controller.config.2.txt
netconf.config.persister.2.properties.numberOfBackups=3
netconf.config.persister.active=3
-netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
+netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
netconf.config.persister.3.properties.fileStorage=target/configuration/current/controller.config.2.txt
netconf.config.persister.3.properties.numberOfBackups=0
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema.Location;
+
+
+/**
+**/
+public class LocationBuilder {
+
+ public static Location getDefaultInstance(String defaultValue) {
+ return defaultValue.equals("NETCONF") ? new Location(Location.Enumeration.NETCONF) : new Location(new Uri(
+ defaultValue));
+ }
+
+}
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-netconf
+ </type>
+
+ <name>instance-from-code</name>
+
+ <ip xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">127.1.2.3</ip>
+ <union-test-attr xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">randomStringForUnion</union-test-attr>
+
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ </services>
+ </config>
+ </edit-config>
+</rpc>
private Boolean throttling = false; // if true, no more batching.
private volatile Boolean shuttingDown = false;
- private LLDPTLV chassisIdTlv, portIdTlv, ttlTlv, customTlv;
+ private LLDPTLV chassisIdTlv, systemNameTlv, portIdTlv, ttlTlv, customTlv;
private IPluginOutConnectionService connectionOutService;
class DiscoveryTransmit implements Runnable {
chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue()).setLength((short) cidValue.length)
.setValue(cidValue);
+ // Create LLDP SystemName TLV
+ byte[] snValue = LLDPTLV.createSystemNameTLVValue(nodeConnector.getNode().toString());
+ systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue()).setLength((short) snValue.length)
+ .setValue(snValue);
+
// Create LLDP PortID TLV
String portId = nodeConnector.getNodeConnectorIDString();
byte[] pidValue = LLDPTLV.createPortIDTLVValue(portId);
// Create discovery pkt
LLDP discoveryPkt = new LLDP();
- discoveryPkt.setChassisId(chassisIdTlv).setPortId(portIdTlv).setTtl(ttlTlv).setOptionalTLVList(customList);
+ discoveryPkt.setChassisId(chassisIdTlv).setPortId(portIdTlv).setTtl(ttlTlv).setSystemNameId(systemNameTlv)
+ .setOptionalTLVList(customList);
RawPacket rawPkt = null;
try {
chassisIdTlv = new LLDPTLV();
chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue());
+ // Create LLDP SystemName TLV
+ systemNameTlv = new LLDPTLV();
+ systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue());
+
// Create LLDP PortID TLV
portIdTlv = new LLDPTLV();
portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue());
public class LLDP extends Packet {
private static final String CHASSISID = "ChassisId";
+ private static final String SYSTEMNAMEID = "SystemNameID";
private static final String PORTID = "PortId";
private static final String TTL = "TTL";
- private static final int LLDPDefaultTlvs = 3;
+ private static final int LLDPDefaultTlvs = 4;
private static LLDPTLV emptyTLV = new LLDPTLV().setLength((short) 0)
.setType((byte) 0);
public static final byte[] LLDPMulticastMac = { 1, (byte) 0x80,
return this;
}
+ /**
+ * @return the SystemName TLV
+ */
+ public LLDPTLV getSystemNameId() {
+ return getTLV(SYSTEMNAMEID);
+ }
+
+ /**
+ * @param LLDPTLV
+ * - the chassisId to set
+ */
+ public LLDP setSystemNameId(LLDPTLV systemNameId) {
+ tlvList.put(getType(SYSTEMNAMEID), systemNameId);
+ return this;
+ }
+
/**
* @return LLDPTLV - the portId TLV
*/
getfieldnumBits(VALUE)); // variable
}
+ /**
+ * Creates the SystemName TLV value
+ *
+ * @param nodeId
+ * node identifier string
+ * @return the SystemName TLV value in byte array
+ */
+ static public byte[] createSystemNameTLVValue(String nodeId) {
+ byte[] nid = nodeId.getBytes();
+ return nid;
+ }
+
/**
* Creates the ChassisID TLV value including the subtype and ChassisID
* string
public static NodeConnector createNodeConnector(
String nodeConnectorType, Object portId, Node node) {
try {
- return new NodeConnector(nodeConnectorType, portId, node);
+ if (nodeConnectorType.equals(Node.NodeIDType.OPENFLOW) && (portId.getClass() == String.class)) {
+ return new NodeConnector(nodeConnectorType, Short.parseShort((String) portId), node);
+ } else {
+ return new NodeConnector(nodeConnectorType, portId, node);
+ }
} catch (ConstructionException e1) {
logger.error("",e1);
return null;
status = updateConfig(conf, isAdding);
if(!status.isSuccess()) {
updateDatabase(conf, (!isAdding));
+ } else {
+ // update the listeners
+ Subnet subnetCurr = subnets.get(conf.getIPAddress());
+ Subnet subnet;
+ if (subnetCurr == null) {
+ subnet = new Subnet(conf);
+ } else {
+ subnet = subnetCurr.clone();
+ }
+ notifySubnetChange(subnet, isAdding);
}
}
<artifactId>topologymanager.integrationtest</artifactId>
<version>0.4.0-SNAPSHOT</version>
<dependencies>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-native</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>protocol_plugins.stub</artifactId>
<module>opendaylight/commons/opendaylight</module>
<module>opendaylight/commons/parent</module>
<module>opendaylight/commons/logback_settings</module>
- <module>opendaylight/commons/controller-maven-plugin</module>
</modules>
<profiles>
<module>opendaylight/statisticsmanager/integrationtest</module>
<module>opendaylight/commons/integrationtest</module>
<module>opendaylight/containermanager/it.implementation</module>
+ <module>opendaylight/distribution/sanitytest/</module>
</modules>
</profile>
<profile>