org.ourgrid.common.executor.vbox.VirtualBoxEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for org.ourgrid.common.executor.vbox.VirtualBoxEnvironment.java

Source

/*
 * Copyright (C) 2008 Universidade Federal de Campina Grande
 *  
 * This file is part of OurGrid. 
 *
 * OurGrid is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free 
 * Software Foundation, either version 3 of the License, or (at your option) 
 * any later version. 
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public License 
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package org.ourgrid.common.executor.vbox;

import static org.ourgrid.common.executor.ProcessUtil.buildAndRunProcess;
import static org.ourgrid.common.executor.ProcessUtil.buildAndRunProcessNoWait;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.ourgrid.common.executor.ExecutorException;
import org.ourgrid.common.executor.ExecutorResult;
import org.ourgrid.common.executor.FolderBasedSandboxedUnixEnvironmentUtil;
import org.ourgrid.common.executor.SandBoxEnvironment;
import org.ourgrid.common.executor.config.ExecutorConfiguration;
import org.ourgrid.common.executor.config.VirtualMachineExecutorConfiguration;
import org.ourgrid.common.util.CommonUtils;
import org.ourgrid.worker.WorkerConstants;

import br.edu.ufcg.lsd.commune.container.logging.CommuneLogger;

/**
 * Executor that performs application executions on a sandboxed environment. The virtual environment
 * is started using predetermined scripts. Currently the only virtual machine supported by this 
 * executor is <i>VirtualBox (http://www.virtualbox.org)</i>.   
 */
public class VirtualBoxEnvironment implements SandBoxEnvironment {

    private static final long serialVersionUID = 40L;

    /*
     * Constants used to build properties file that will be transfered to the virtual machine. The
     * file has the following syntax.
     * 
     * APP_SCRIPT="value"
     * APP_EXIT="value"
     * APP_STDOUT="value"
     * APP_STDERR="value"
     * TERMINATION_FILE="value" 
     * STORAGE_NAME="value" 
     */
    private static final String VIRTUAL_ENV_APP_SCRIPT_PROP = "APP_SCRIPT";
    private static final String VIRTUAL_ENV_APP_EXIT_PROP = "APP_EXIT";
    private static final String VIRTUAL_ENV_APP_STDOUT_PROP = "APP_STDOUT";
    private static final String VIRTUAL_ENV_APP_STDERR_PROP = "APP_STDERR";
    private static final String VIRTUAL_ENV_TERMINATION_FILE_PROP = "TERMINATION_FILE";
    private static final String VIRTUAL_ENV_STORAGE_NAME_PROP = "STORAGE_NAME";
    private static final String VIRTUAL_ENV_PROPS_FILES = "OG_OPTS";

    /*
     * Constants that determine default names for output files.
     */
    private static final String APP_EXIT = "app.exit";
    private static final String APP_STDERR = "app.stderr";
    private static final String APP_STDOUT = "app.stdout";
    private static final String APP_SCRIPT_SH = "app.script.sh";
    private static final String TERMINATED = ".terminated";
    private static final String VBOX_VM_STORAGE = "storage";

    /*
     * Location of the virtual environment properties file. 
     */
    private File virtualEnvPropertiesFile;

    /*
     * Configured commands.
     */
    private String startvmCmd;
    private String stopvmCmd;
    private String machineName;
    private String vBoxLocation;

    /*
     * Class parameters to manipulate executions.
     */
    private File terminationFile;
    private File execFile;
    private File stdOut;
    private File stdErr;
    private File exitStatus;
    private File vmStorage;
    private File playpen;
    private File storage;

    /*
     * Other
     */
    private final FolderBasedSandboxedUnixEnvironmentUtil unixFolderUtil;

    private CommuneLogger logger;

    private Process execProcess;

    /**
     * Creates a new <code>VirtualMachineExecutor</code> using the
     * given <code>Executor</code> to perform tasks.
     * 
     * @param osExecutor <code>Executor</code> that will execute scripts.
     * @param logger 
     */
    public VirtualBoxEnvironment(CommuneLogger logger) {
        this.unixFolderUtil = new FolderBasedSandboxedUnixEnvironmentUtil();
        this.logger = logger;
    }

    public void setConfiguration(ExecutorConfiguration executorConfiguratrion) {
        this.startvmCmd = ("\""
                + executorConfiguratrion.getProperty(VirtualMachineExecutorConfiguration.PROPERTY_START_VM_COMMAND)
                + "\"");
        this.stopvmCmd = ("\""
                + executorConfiguratrion.getProperty(VirtualMachineExecutorConfiguration.PROPERTY_STOP_VM_COMMAND)
                + "\"");
        this.vBoxLocation = ("\""
                + executorConfiguratrion.getProperty(VirtualMachineExecutorConfiguration.PROPERTY_VBOX_LOCATION)
                + "\"");
        this.machineName = executorConfiguratrion
                .getProperty(VirtualMachineExecutorConfiguration.PROPERTY_MACHINE_NAME);
    }

    private void initEnvironmentVariables(Map<String, String> envVars) throws ExecutorException {
        String uniquifier = Integer.toString((int) (Math.random() * Integer.MAX_VALUE));

        this.playpen = new File(envVars.get(WorkerConstants.ENV_PLAYPEN));
        this.storage = new File(envVars.get(WorkerConstants.ENV_STORAGE));
        this.vmStorage = new File(playpen.getAbsolutePath() + File.separator + uniquifier + VBOX_VM_STORAGE);
        this.terminationFile = new File(playpen.getAbsolutePath() + File.separator + uniquifier + TERMINATED);
        this.execFile = new File(playpen.getAbsolutePath() + File.separator + uniquifier + APP_SCRIPT_SH);
        this.stdOut = new File(playpen.getAbsolutePath() + File.separator + uniquifier + APP_STDOUT);
        this.stdErr = new File(playpen.getAbsolutePath() + File.separator + uniquifier + APP_STDERR);
        this.exitStatus = new File(playpen.getAbsolutePath() + File.separator + uniquifier + APP_EXIT);
        this.virtualEnvPropertiesFile = new File(
                playpen.getAbsolutePath() + File.separator + VIRTUAL_ENV_PROPS_FILES);
    }

    public Process executeRemoteCommand(String dirName, String command, Map<String, String> envVars)
            throws ExecutorException {
        try {

            //         String env_storage = envVars.get(WorkerConstants.ENV_STORAGE);

            //         command = command.replace(env_storage, vmStorage.getName());
            getLogger().info("Asked to run command " + command);

            //Defining new environment variables
            Map<String, String> clone = CommonUtils.createSerializableMap();
            clone.putAll(envVars);
            clone.remove(WorkerConstants.ENV_PLAYPEN);
            clone.remove(WorkerConstants.ENV_STORAGE);
            clone.put(WorkerConstants.PROP_STORAGE_DIR, vmStorage.getName());

            //Creating application script
            File script = unixFolderUtil.createScript(command, dirName, clone);

            //Writing virtual environment variables
            PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(virtualEnvPropertiesFile)));
            writer.println(VIRTUAL_ENV_APP_SCRIPT_PROP + "=" + execFile.getName());
            writer.println(VIRTUAL_ENV_APP_EXIT_PROP + "=" + exitStatus.getName());
            writer.println(VIRTUAL_ENV_APP_STDOUT_PROP + "=" + stdOut.getName());
            writer.println(VIRTUAL_ENV_APP_STDERR_PROP + "=" + stdErr.getName());
            writer.println(VIRTUAL_ENV_TERMINATION_FILE_PROP + "=" + terminationFile.getName());
            writer.println(VIRTUAL_ENV_STORAGE_NAME_PROP + "=" + vmStorage.getName());

            try {
                if (writer.checkError()) {
                    throw new IOException("Unable to create Virtual environment");
                }
            } finally {
                writer.close();
            }

            //Copying files to needed location
            FileUtils.copyFile(script, execFile);
            unixFolderUtil.copyStorageFiles(storage, vmStorage);

        } catch (IOException e) {
            throw new ExecutorException("Unable to create remote execution script", e);
        }

        getLogger().debug("About to start secure environment");

        //Executing command that will initiate virtual environment and execute the process
        Process execProcess = buildAndRunProcessNoWait(createStartVmAndExecuteCmd(), "Could not execute command");
        setExecProcess(execProcess);

        getLogger().debug("About to wait for secure environment to exit");

        return execProcess;
    }

    private List<String> createStartVmAndExecuteCmd() {
        List<String> cmd = new LinkedList<String>();
        cmd.add(startvmCmd);
        cmd.add(vBoxLocation);
        cmd.add(machineName);
        cmd.add(getPlayPenPath());

        return cmd;
    }

    private String getPlayPenPath() {
        return "\"" + playpen.getAbsolutePath() + "\"";
    }

    public ExecutorResult getResult() throws ExecutorException {

        if (!isExecutionInProcess()) {
            throw new ExecutorException("No execution is in process, probably it was killed.");
        }

        //Acquiring results
        ExecutorResult result = new ExecutorResult();
        try {
            unixFolderUtil.copyStorageFiles(vmStorage, storage);
            unixFolderUtil.catchOutputFromFile(result, stdOut, stdErr, exitStatus);

        } catch (Throwable e) {
            throw new ExecutorException("Unable to catch output ", e);
        } finally {
            stopVm();
            cleanup();
        }

        return result;
    }

    public void killVm() throws ExecutorException {

        if (!isExecutionInProcess()) {
            throw new ExecutorException("No execution is in process");
        }
        stopVm();
        cleanup();
    }

    private void stopVm() throws ExecutorException {

        getLogger().debug("About to kill secure environment");
        buildAndRunProcess(createStopVmCommand(), "Unable to kill virtual environment");

        Process execProcess = getExecProcess();

        if (execProcess != null) {
            execProcess.destroy();
        }
    }

    private List<String> createStopVmCommand() {
        List<String> cmd = new LinkedList<String>();
        cmd.add(stopvmCmd);
        cmd.add(vBoxLocation);
        cmd.add(machineName);

        return cmd;
    }

    private void cleanup() {
        this.playpen = null;
        this.vmStorage = null;
        this.terminationFile = null;
        this.execFile = null;
        this.stdOut = null;
        this.stdErr = null;
        this.exitStatus = null;
    }

    protected boolean isExecutionInProcess() {
        return playpen != null;
    }

    public void chmod(File file, String perm) throws ExecutorException {

    }

    public void finishExecution() throws ExecutorException {

    }

    public CommuneLogger getLogger() {
        return logger;
    }

    public boolean hasExecutionFinished() throws ExecutorException {
        return exitStatus.exists();
    }

    public void initSandboxEnvironment(Map<String, String> envVars) throws ExecutorException {
        initEnvironmentVariables(envVars);
    }

    public void shutDownSandBoxEnvironment() throws ExecutorException {
        killVm();
    }

    public void stopPrepareAllocation() throws ExecutorException {
        stopVm();
        cleanup();
    }

    public Process prepareAllocation() throws ExecutorException {
        return null;
    }

    public void setExecProcess(Process execProcess) {
        this.execProcess = execProcess;
    }

    public Process getExecProcess() {
        return execProcess;
    }

}