Merge "Improve logging of persister and netconf client."
authorGiovanni Meo <gmeo@cisco.com>
Wed, 11 Dec 2013 14:39:56 +0000 (14:39 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 11 Dec 2013 14:39:56 +0000 (14:39 +0000)
19 files changed:
opendaylight/commons/controller-maven-plugin/pom.xml [new file with mode: 0644]
opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/AbstractControllerMojo.java [new file with mode: 0644]
opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/StartControllerMojo.java [new file with mode: 0644]
opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/StopControllerMojo.java [new file with mode: 0644]
opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/JavaProcess.java [new file with mode: 0644]
opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/JpsProcessMonitor.java [new file with mode: 0644]
opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/ProcessMonitor.java [new file with mode: 0644]
opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/VMProcessMonitor.java [new file with mode: 0644]
opendaylight/commons/opendaylight/pom.xml
opendaylight/distribution/opendaylight/pom.xml
opendaylight/distribution/opendaylight/runsanity.bat [deleted file]
opendaylight/distribution/opendaylight/runsanity.sh [deleted file]
opendaylight/distribution/opendaylight/src/assemble/bin.xml
opendaylight/distribution/opendaylight/src/main/resources/run.bat
opendaylight/distribution/sanitytest/pom.xml
opendaylight/distribution/sanitytest/src/main/java/org/opendaylight/controller/distribution/Sanity.java [deleted file]
opendaylight/distribution/sanitytest/src/main/java/org/opendaylight/controller/sanitytest/internal/Activator.java [deleted file]
opendaylight/distribution/sanitytest/src/test/java/org/opendaylight/controller/distribution/test/SanityIT.java [new file with mode: 0644]
pom.xml

diff --git a/opendaylight/commons/controller-maven-plugin/pom.xml b/opendaylight/commons/controller-maven-plugin/pom.xml
new file mode 100644 (file)
index 0000000..1ab4e70
--- /dev/null
@@ -0,0 +1,83 @@
+<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>
diff --git a/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/AbstractControllerMojo.java b/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/AbstractControllerMojo.java
new file mode 100644 (file)
index 0000000..39ca71c
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * 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();
+    }
+
+}
diff --git a/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/StartControllerMojo.java b/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/StartControllerMojo.java
new file mode 100644 (file)
index 0000000..0a3bee4
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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;
+    }
+}
diff --git a/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/StopControllerMojo.java b/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/StopControllerMojo.java
new file mode 100644 (file)
index 0000000..579778d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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();
+    }
+
+}
diff --git a/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/JavaProcess.java b/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/JavaProcess.java
new file mode 100644 (file)
index 0000000..95da34f
--- /dev/null
@@ -0,0 +1,57 @@
+
+/*
+ * 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();
+    }
+}
diff --git a/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/JpsProcessMonitor.java b/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/JpsProcessMonitor.java
new file mode 100644 (file)
index 0000000..8474266
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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;
+    }
+
+}
diff --git a/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/ProcessMonitor.java b/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/ProcessMonitor.java
new file mode 100644 (file)
index 0000000..5c3c69f
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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());
+        }
+    }
+
+}
diff --git a/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/VMProcessMonitor.java b/opendaylight/commons/controller-maven-plugin/src/main/java/org/opendaylight/controller/maven/plugin/util/VMProcessMonitor.java
new file mode 100644 (file)
index 0000000..fdf232a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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;
+    }
+
+
+}
index dd73815..07c8916 100644 (file)
@@ -70,6 +70,8 @@
     <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>
index 4c0b81f..033d057 100644 (file)
       </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-dependency-plugin</artifactId>
-            <version>2.8</version>
-            <executions>
-              <execution>
-                <id>copy</id>
-                <phase>package</phase>
-                <goals>
-                  <goal>copy</goal>
-                </goals>
-              </execution>
-            </executions>
+            <artifactId>maven-invoker-plugin</artifactId>
+            <version>1.5</version>
             <configuration>
-              <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>
+              <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>
         </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>
diff --git a/opendaylight/distribution/opendaylight/runsanity.bat b/opendaylight/distribution/opendaylight/runsanity.bat
deleted file mode 100644 (file)
index f219828..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-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
diff --git a/opendaylight/distribution/opendaylight/runsanity.sh b/opendaylight/distribution/opendaylight/runsanity.sh
deleted file mode 100755 (executable)
index 4ee9555..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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
-
index 8fea175..1e8f34e 100644 (file)
@@ -27,7 +27,6 @@
         <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}
index 9d6ac8d..a8e33d2 100644 (file)
@@ -140,7 +140,7 @@ SET RUN_CMD="%JAVA_HOME%\bin\java.exe" -Dopendaylight.controller %extraJVMOpts%
 ECHO %RUN_CMD%
 
 if "%startEnabled%" NEQ "" (
-    START /B cmd /C CALL %RUN_CMD% > %basedir%\logs\controller.out 2>&1
+    START /B cmd /C CALL %RUN_CMD%
     ECHO Running controller in the background.
 ) else (
     %RUN_CMD%
index 9d5ba5c..ce710d1 100644 (file)
 
   <artifactId>sanitytest</artifactId>
   <version>0.4.1-SNAPSHOT</version>
-  <packaging>bundle</packaging>
+  <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>
+
   <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>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <version>${bundle.plugin.version}</version>
-        <extensions>true</extensions>
+        <!-- disable the install plugin -->
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-install-plugin</artifactId>
+        <version>${install.plugin.version}</version>
         <configuration>
-          <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>
+          <skip>true</skip>
         </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>
diff --git a/opendaylight/distribution/sanitytest/src/main/java/org/opendaylight/controller/distribution/Sanity.java b/opendaylight/distribution/sanitytest/src/main/java/org/opendaylight/controller/distribution/Sanity.java
deleted file mode 100644 (file)
index 7fc25e2..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-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());
-    }
-}
diff --git a/opendaylight/distribution/sanitytest/src/main/java/org/opendaylight/controller/sanitytest/internal/Activator.java b/opendaylight/distribution/sanitytest/src/main/java/org/opendaylight/controller/sanitytest/internal/Activator.java
deleted file mode 100644 (file)
index 08f0700..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-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 {
-
-    }
-}
diff --git a/opendaylight/distribution/sanitytest/src/test/java/org/opendaylight/controller/distribution/test/SanityIT.java b/opendaylight/distribution/sanitytest/src/test/java/org/opendaylight/controller/distribution/test/SanityIT.java
new file mode 100644 (file)
index 0000000..931e88f
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * 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;
+        }
+    }
+
+
+
+
+}
diff --git a/pom.xml b/pom.xml
index f7f9bc2..6ece420 100644 (file)
--- a/pom.xml
+++ b/pom.xml
     <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>