/* * 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.vserver; import static org.ourgrid.common.executor.ProcessUtil.buildAndRunProcess; import static org.ourgrid.common.executor.ProcessUtil.buildAndRunProcessNoWait; import static org.ourgrid.common.executor.ProcessUtil.parseCommand; import java.io.File; import java.io.IOException; 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.VServerExecutorConfiguration.PROPERTIES; import org.ourgrid.common.util.CommonUtils; import org.ourgrid.worker.WorkerConstants; import br.edu.ufcg.lsd.commune.container.logging.CommuneLogger; /** * This entity provides an abstraction layer to handle commands execution on the * Linux VServer kernel. The Linux VServer project implements a OS based virtualization approach. * * The current OurGrid VServer workflow look like this: * * 1. The playpen directory is MAPPED in a directory inside the virtual environment. * 2. The ourgrid storage is copied to a directory inside the virtual environment. * 3. The remote execution command held by a script is created on the virtual environment. * 4. A blocking wait is executed, looking up to a termination file. * 5. In the end of execution the virtual storage directory is cleaned. * * A new virtual machine is created, started and destroied in every execution. * * @see http://linux-vserver.org/Welcome_to_Linux-VServer.org * * @author Thiago Emmanuel Pereira da Cunha Silva, thiago.manel@gmail.com * since 21/07/2007 */ public class VServerSandBoxedEnvironment implements SandBoxEnvironment { private static final long serialVersionUID = -2576479282260443738L; /* These file contains the standard and error execution output. Also the execution exit value */ private File stdOutput; private File errorOutput; private File exitValue; private final FolderBasedSandboxedUnixEnvironmentUtil unixFolderUtil = new FolderBasedSandboxedUnixEnvironmentUtil(); private File playPenVmRoot; private File storageVmRoot; private String host_playpen_path; private String host_storage_path; private String playPenVmDir; private String storageVmDir; private CommuneLogger logger; private String vmName; private List<String> startvmCmd; private List<String> stopvmCmd; private List<String> verifyStatusCmd; private List<String> execCmd; private List<String> prepareAllocationCmd; private List<String> stopPrepareAllocationCmd; private String copyCommandScript; private String replaceImageCommandScript; private String killTarProcScript; private File ourGridAppFile; private ExecutorConfiguration configuration; public VServerSandBoxedEnvironment(CommuneLogger logger) { this.logger = logger; } /* (non-Javadoc) * @see org.ourgrid.common.executor.SandBoxEnvironment#setConfiguration(org.ourgrid.common.executor.config.ExecutorConfiguration) */ public void setConfiguration(ExecutorConfiguration executorConfiguration) { this.vmName = executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.VM_NAME.toString()); this.prepareAllocationCmd = parseCommand(executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.PREPARE_ALLOCATION_COMMAND.toString())); this.startvmCmd = parseCommand(executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.START_VM_COMMAND.toString())); this.stopvmCmd = parseCommand(executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.STOP_VM_COMMAND.toString())); this.verifyStatusCmd = parseCommand(executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.STATUS_VM_COMMAND.toString())); this.execCmd = parseCommand(executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.EXEC_COMMAND.toString())); this.stopPrepareAllocationCmd = parseCommand(executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.STOP_PREPARING_ALLOCATION_COMMAND.toString())); this.copyCommandScript = executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.COPY_FILES_COMMAND.toString()); this.replaceImageCommandScript = executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.REPLACE_VM_IMAGE_COMMAND.toString()); this.killTarProcScript = executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.KILL_TAR_PROC_COMMAND.toString()); this.playPenVmRoot = new File(executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.VM_PLAYPEN.toString())); this.storageVmRoot = new File(executorConfiguration.getProperty(WorkerConstants.PREFIX + PROPERTIES.VM_STORAGE.toString())); this.host_playpen_path = ""; this.host_storage_path = ""; this.storageVmDir = ""; this.playPenVmDir = ""; this.stdOutput = new File(""); this.errorOutput = new File(""); this.exitValue = new File(""); this.configuration = executorConfiguration; } public Process prepareAllocation() throws ExecutorException { return buildAndRunProcessNoWait( createBeginAllocationCommand(), "Could not preparing allocation"); } private List<String> createBeginAllocationCommand() { List<String> beginAlloc = new LinkedList<String>(prepareAllocationCmd); beginAlloc.add(vmName); beginAlloc.add(replaceImageCommandScript); return beginAlloc; } /* (non-Javadoc) * @see org.ourgrid.common.executor.SandboxedExecutor#initSandboxEnvironment() */ public void initSandboxEnvironment(Map<String, String> envVars) throws ExecutorException { this.host_playpen_path = envVars.get(WorkerConstants.ENV_PLAYPEN); this.host_storage_path = envVars.get(WorkerConstants.ENV_STORAGE); File host_playpenDir = new File(host_playpen_path); File host_storageDir = new File(host_storage_path); playPenVmDir = playPenVmRoot.getAbsolutePath() + File.separator + host_playpenDir.getName(); storageVmDir = storageVmRoot.getAbsolutePath() + File.separator + host_storageDir.getName(); try { this.ourGridAppFile = new File(host_playpen_path, configuration.getProperty(WorkerConstants.PREFIX + PROPERTIES.APP_SCRIPT.toString())); this.stdOutput = new File(host_playpen_path, configuration.getProperty(WorkerConstants.PREFIX + PROPERTIES.APP_STDOUT_FILE_NAME.toString())); this.errorOutput = new File(host_playpen_path, configuration.getProperty(WorkerConstants.PREFIX + PROPERTIES.APP_STDERROR_FILE_NAME.toString())); this.exitValue = new File(host_playpen_path, configuration.getProperty(WorkerConstants.PREFIX + PROPERTIES.TERMINATION_FILE_NAME.toString())); } catch (NullPointerException e) { throw new ExecutorException(e); } buildAndRunProcess(createInitCommand(), "Could not init VServer"); } private List<String> createInitCommand() { List<String> initCommand = new LinkedList<String>(startvmCmd); initCommand.add(vmName); initCommand.add(host_playpen_path); initCommand.add(host_storage_path); initCommand.add(storageVmRoot.getAbsolutePath()); initCommand.add(copyCommandScript); return initCommand; } /* (non-Javadoc) * @see org.ourgrid.common.executor.SandBoxEnvironment#executeRemoteCommand(java.lang.String, java.lang.String, java.util.Map) */ public Process executeRemoteCommand(String dirName, String command, Map<String, String> envVars) throws ExecutorException { logger.info( "Asked to run remote command " + command); if(! isSandBoxUp()){ IllegalStateException illegalStateException = new IllegalStateException("VServer environment is not running. Can not execute commands."); throw new ExecutorException( illegalStateException ); } Map<String, String> clone = CommonUtils.createSerializableMap(); clone.putAll(envVars); clone.remove(WorkerConstants.PROP_PLAYPEN_ROOT); clone.remove(WorkerConstants.PROP_STORAGE_DIR); clone.put(WorkerConstants.PROP_PLAYPEN_ROOT, playPenVmDir); clone.put(WorkerConstants.PROP_STORAGE_DIR, storageVmDir); //The VServer executor must run in the playpen directory. /* Command execution script. This one runs in the vserver secure environment.*/ File remoteScript = unixFolderUtil.createScript( command, host_playpen_path, clone ); try { FileUtils.copyFile(remoteScript, ourGridAppFile); } catch (IOException e) { throw new ExecutorException("Unable to create remote execution script", e); } return executeRemoteCommand(playPenVmDir); } /** * @param dirName * @throws ExecutorException */ private Process executeRemoteCommand(String dirName) throws ExecutorException{ return buildAndRunProcessNoWait(createExecCommand(dirName), "Could not execute command"); } /** * @see vserver_exec script * * @param dirName * @return */ private List<String> createExecCommand(String dirName) { List<String> exec = new LinkedList<String>(execCmd); exec.add(dirName); exec.add(vmName); exec.add(playPenVmDir); exec.add(storageVmDir); exec.add(ourGridAppFile.getName()); exec.add(stdOutput.getName()); exec.add(errorOutput.getName()); exec.add(exitValue.getName()); return exec; } /** * @return * @throws ExecutorException */ private boolean isSandBoxUp() throws ExecutorException { return buildAndRunProcess(createVerifyCommand(), "Could not verify Vserver state: "+vmName); } private List<String> createVerifyCommand() { List<String> verify = new LinkedList<String>(verifyStatusCmd); verify.add(vmName); return verify; } /* (non-Javadoc) * @see org.ourgrid.common.executor.SandBoxEnvironment#shutDownSandBoxEnvironment() */ public void shutDownSandBoxEnvironment() throws ExecutorException { stopVserver(host_playpen_path); cleanEnvironment(); } public void stopPrepareAllocation() throws ExecutorException { buildAndRunProcess( createStopPreparingAllocation(), "Could not stop preparing allocation"); } /** * Stop VServer VM and delete the output files staged out from the VM * @param host_playpen * @throws ExecutorException */ private void stopVserver(String host_playpen) throws ExecutorException{ buildAndRunProcess( createStopCommand(), "Could not stop VServer"); } private void cleanEnvironment() { deleteFile(stdOutput, "Could not delete output file" + stdOutput.getAbsolutePath()); deleteFile(errorOutput, "Could not delete error output file" + errorOutput.getAbsolutePath()); deleteFile(exitValue, "Could not delete exit value file" + exitValue.getAbsolutePath()); } private List<String> createStopCommand() { List<String> stop = new LinkedList<String>(stopvmCmd); stop.add(vmName); stop.add(host_playpen_path); stop.add(host_storage_path); stop.add(storageVmDir); stop.add(copyCommandScript); return stop; } private List<String> createStopPreparingAllocation() { List<String> stop = new LinkedList<String>(stopPrepareAllocationCmd); stop.add(vmName); stop.add(killTarProcScript); return stop; } /* (non-Javadoc) * @see org.ourgrid.common.executor.SandBoxEnvironment#getResult() */ public ExecutorResult getResult() throws ExecutorException { buildAndRunProcess( createStopCommand(), "Could not stop VServer"); logger.debug( "Getting result of execution..." ); ExecutorResult result = new ExecutorResult(); try{ unixFolderUtil.catchOutputFromFile(result, stdOutput, errorOutput, exitValue); } catch (Exception e) { throw new ExecutorException("Unable to catch output ", e); } logger.debug( "Finished getResult. Single Execution released." ); return result; } private void deleteFile(File file, String erroMsg) { if ( file.delete() == false ) { logger.error( "Could not delete file: " + erroMsg ); } } /* (non-Javadoc) * @see org.ourgrid.common.executor.SandBoxEnvironment#hasExecutionFinished() */ public boolean hasExecutionFinished() throws ExecutorException { return exitValue.exists(); } public void finishExecution() throws ExecutorException { // TODO Auto-generated method stub } public CommuneLogger getLogger() { return logger; } }