r1754 - in isis-fish/trunk: . src/main/java/fr/ifremer/isisfish src/main/java/fr/ifremer/isisfish/simulator/launcher src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh src/main/resources src/main/resources/i18n src/main/resources/ssh src/test/java/fr/ifremer/isisfish/simulator src/test/java/fr/ifremer/isisfish/simulator/launcher
Author: chatellier Date: 2009-01-23 17:37:09 +0000 (Fri, 23 Jan 2009) New Revision: 1754 Added: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHSimulatorLauncher.java isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/ClasspathTemplateLoader.java isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHException.java isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHUserInfo.java isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHUtils.java isis-fish/trunk/src/main/resources/ssh/ isis-fish/trunk/src/main/resources/ssh/launch-isis-pxe.seq isis-fish/trunk/src/main/resources/ssh/launch-isis.seq isis-fish/trunk/src/test/java/fr/ifremer/isisfish/simulator/launcher/ isis-fish/trunk/src/test/java/fr/ifremer/isisfish/simulator/launcher/SshSimulatorLauncherTest.java Removed: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHUtils.java isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SshSimulatorLauncher.java Modified: isis-fish/trunk/pom.xml isis-fish/trunk/src/main/java/fr/ifremer/isisfish/IsisConfig.java isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SimulationJob.java isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SimulatorLauncher.java isis-fish/trunk/src/main/resources/i18n/isis-fish-en_GB.properties isis-fish/trunk/src/main/resources/i18n/isis-fish-fr_FR.properties Log: Mise ?\195?\160 jour du launcher SSH sur caparmor Modified: isis-fish/trunk/pom.xml =================================================================== --- isis-fish/trunk/pom.xml 2009-01-23 09:22:36 UTC (rev 1753) +++ isis-fish/trunk/pom.xml 2009-01-23 17:37:09 UTC (rev 1754) @@ -11,7 +11,7 @@ <parent> <groupId>org.codelutin</groupId> <artifactId>lutinproject</artifactId> - <version>3.4-SNAPSHOT</version> + <version>3.3</version> </parent> <groupId>ifremer</groupId> @@ -208,7 +208,7 @@ <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> - <version>1.1.105</version> + <version>1.1.106</version> <scope>compile</scope> </dependency> @@ -292,6 +292,11 @@ </dependency> <!-- fin svnkit pour communication subversion --> + <dependency> + <groupId>org.freemarker</groupId> + <artifactId>freemarker</artifactId> + <version>2.3.15</version> + </dependency> </dependencies> <!-- ************************************************************* --> @@ -334,7 +339,6 @@ <!-- Ignore failure test for now --> <maven.test.testFailureIgnore>true</maven.test.testFailureIgnore> - <maven.javadoc.skip>true</maven.javadoc.skip> <!-- jnlp --> <keystorepath>../../../CodeLutinKeystore</keystorepath> Modified: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/IsisConfig.java =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/IsisConfig.java 2009-01-23 09:22:36 UTC (rev 1753) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/IsisConfig.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -50,7 +50,7 @@ import fr.ifremer.isisfish.simulator.SimulationContext; import fr.ifremer.isisfish.simulator.launcher.InProcessSimulatorLauncher; import fr.ifremer.isisfish.simulator.launcher.SimulationService; -import fr.ifremer.isisfish.simulator.launcher.SshSimulatorLauncher; +import fr.ifremer.isisfish.simulator.launcher.SSHSimulatorLauncher; import fr.ifremer.isisfish.simulator.launcher.SubProcessSimulationLauncher; import fr.ifremer.isisfish.vcs.VCS; @@ -633,7 +633,7 @@ /** prevu pour l'architecture de lancement en plugin: local, isis-server, caparmor, ... */ SIMULATOR_LAUNCHER(SimulationService.SIMULATION_LAUNCHER + ".localDefault", _("isisfish.config.main.localSimulator.description"), InProcessSimulatorLauncher.class.getName()), SIMULATOR_LAUNCHER_SUB(SimulationService.SIMULATION_LAUNCHER + ".subDefault", _("isisfish.config.main.subSimulator.description"), SubProcessSimulationLauncher.class.getName()), - SIMULATOR_LAUNCHER_REMOTE(SimulationService.SIMULATION_LAUNCHER + ".remoteCaparmor", _("isisfish.config.main.remoteCaparmor.description"), SshSimulatorLauncher.class.getName()), + SIMULATOR_LAUNCHER_REMOTE(SimulationService.SIMULATION_LAUNCHER + ".remoteCaparmor", _("isisfish.config.main.remoteCaparmor.description"), SSHSimulatorLauncher.class.getName()), SIMULATOR_SERVER("simulation.server", _("isisfish.config.main.simulationServer.description"), "http://simulateur.ifremer.fr:9090"), /** le login a utiliser pour les launcher distant, le type du launcher est ajouter a la cle (car 1 login par launcher) */ Copied: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHSimulatorLauncher.java (from rev 1751, isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SshSimulatorLauncher.java) =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHSimulatorLauncher.java (rev 0) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHSimulatorLauncher.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -0,0 +1,694 @@ +/* *##% + * Copyright (C) 2008, 2009 Code Lutin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +package fr.ifremer.isisfish.simulator.launcher; + +import static org.codelutin.i18n.I18n._; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.rmi.RemoteException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codelutin.util.FileUtil; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UserInfo; + +import fr.ifremer.isisfish.IsisFish; +import fr.ifremer.isisfish.datastore.SimulationStorage; +import fr.ifremer.isisfish.simulator.SimulationControl; +import fr.ifremer.isisfish.simulator.launcher.ssh.ClasspathTemplateLoader; +import fr.ifremer.isisfish.simulator.launcher.ssh.SSHException; +import fr.ifremer.isisfish.simulator.launcher.ssh.SSHUserInfo; +import fr.ifremer.isisfish.simulator.launcher.ssh.SSHUtils; +import freemarker.cache.TemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; + +/** + * Use a remote simulation server. + * + * Upload zip simulation file on server and launch + * simulation on that file. + * + * Isis-Fish must be installed on remote server. + * + * @see JSch + * + * @author chatellier + * @version $Revision: 1.0 $ + * + * Last update : $Date: 18 nov. 2008 $ + * By : $Author: chatellier $ + */ +public class SSHSimulatorLauncher implements SimulatorLauncher { + + /** Class logger */ + protected static Log log = LogFactory.getLog(SSHSimulatorLauncher.class); + + /** Freemarker configuration */ + protected Configuration freemarkerConfiguration; + + /** + * Constructor. + * + * Init freemarker. + */ + public SSHSimulatorLauncher() { + initFreemarker(); + } + + /** + * Init freemarker configuration. + */ + protected void initFreemarker() { + + freemarkerConfiguration = new Configuration(); + + // needed to overwrite "Defaults to default system encoding." + // fix encoding issue on some systems + freemarkerConfiguration.setDefaultEncoding("utf-8"); + + // specific template loader to get template from jars (classpath) + TemplateLoader templateLoader = new ClasspathTemplateLoader(); + freemarkerConfiguration.setTemplateLoader(templateLoader); + + //freemarkerConfiguration.setObjectWrapper(new BeansWrapper()); + + } + + /** + * Display message both in commons-logging and control text progress. + * + * @param control control + * @param message message to display + */ + protected void message(SimulationControl control, String message) { + // log + if (log.isInfoEnabled()) { + log.info(message); + } + // control + if (control != null) { + control.setText(message); + } + } + + /* + * @see fr.ifremer.isisfish.simulator.launcher.SimulatorLauncher#maxSimulationThread() + */ + @Override + public int maxSimulationThread() { + + // FIXME pour toutes les envoyer + // met le serveur les lance quand il veut + + return 1; + } + + /* + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return _("isisfish.simulator.launcher.remote"); + } + + /** + * {@inheritDoc} + * + * Dans le cas de ssh: + * - upload la simulation + * - construit le script pour qsub + * - upload le script qsub + * - ajoute le script a qsub + * + * Et : + * - lance le thread de control de la simulation + */ + @Override + public SimulationStorage simulate(SimulationService simulationService, + SimulationControl control, File simulationZip) + throws RemoteException { + + SimulationStorage simulationStorage = null; + + // start ssh session + try { + + // connection + message(control, _("isisfish.simulation.remote.message.connection")); + Session sshSession = openSSHSession(); + + // upload simulation on server + message(control, _("isisfish.simulation.remote.message.upload")); + String simulationRemotePath = uploadSimulation(sshSession, simulationZip); + + // build du contenu du script + message(control, _("isisfish.simulation.remote.message.waitingstart")); + File simulationScript = getSimulationScriptFile(control.getId(), simulationRemotePath); + String scriptRemotePath = uploadSimulationScript(sshSession, simulationScript); + addScriptToQsubQueue(sshSession, scriptRemotePath); + + // just start a thread + Thread thread = checkSimulationProgression(sshSession, simulationService, + control); + try { + thread.join(); + } catch (InterruptedException e) { + if (log.isErrorEnabled()) { + log.error(_("Control thread interrupted"), e); + } + } + + // recuperation des resultats + message(control, + _("isisfish.simulation.remote.message.downloadresults")); + downloadResults(sshSession, control.getId()); + simulationStorage = SimulationStorage.getSimulation(control.getId()); + + // force thread to stop + synchronized (control) { + control.stopSimulation(); + } + + } catch (Exception e) { + if (log.isErrorEnabled()) { + log.error(_("isisfish.error.simulation.remote.global")); + } + throw new RemoteException( + _("isisfish.error.simulation.remote.global"), e); + } + + return simulationStorage; + } + + /** + * Connect to remote server throw SSH, and return session. + * + * @return valid opened session + * + * @throws JSchException + */ + protected Session openSSHSession() throws JSchException { + + JSch jsch = new JSch(); + + // extract connection infos + String host = IsisFish.config.getSimulatorSshServer(); + String username = IsisFish.config.getSimulatorSshUsername(); + + int port = 22; // by default, 22 + + if (host.indexOf(':') > 0) { + String sPort = host.substring(host.indexOf(':') + 1); + try { + port = Integer.parseInt(sPort); + } catch (NumberFormatException e) { + if (log.isWarnEnabled()) { + log.warn(_( + "isisfish.error.simulation.remote.wrongportvalue", + sPort)); + } + } + host = host.substring(0, host.indexOf(':')); + } + + if (log.isInfoEnabled()) { + log.info(_("Try to log on %s@%s:%d", username, host, port)); + } + + // add ssh key + boolean sshKeyUsed = false; + File sshKey = IsisFish.config.getSSHPrivateKeyFilePath(); + if (sshKey.canRead()) { + if (log.isInfoEnabled()) { + log.info(_("Ssh key found '%s' will be used to connect to", + sshKey.getAbsoluteFile(), host)); + } + jsch.addIdentity(sshKey.getAbsolutePath()); + sshKeyUsed = true; + } + + Session session = jsch.getSession(username, host, port); + + // username and password will be given via UserInfo interface. + UserInfo ui = new SSHUserInfo(); + session.setUserInfo(ui); + session.connect(60000); // timeout + + // test here, if password has been asked to user + if (session.isConnected() && sshKeyUsed && ui.getPassword() != null) { + putSshKeyOnRemoteServer(session, sshKey); + } + return session; + } + + /** + * Close ssh session. + * + * @param session session to close + */ + protected void closeSSHSession(Session session) { + if (session != null) { + session.disconnect(); + } + } + + /** + * Add ssh key into $HOME/.ssh/authorized_keys file. + * + * Just connect and do an "echo xx >> .ssh/authorized_keys" + * @param session opened session + * @param sshKey + * @throws JSchException + */ + protected void putSshKeyOnRemoteServer(Session session, File sshKey) + throws JSchException { + + // get public key for argument private key file + File publicKey = new File(sshKey.getAbsoluteFile() + ".pub"); + + // command to : + // - make ssh directory + // - add key to authorized_keys + String command = "if [ ! -d .ssh ] ; then mkdir .ssh; fi ; echo \"%s\" >> .ssh/authorized_keys"; + + try { + // use usefull readLines from commons-io + @SuppressWarnings("unchecked") + List<String> contents = FileUtils.readLines(publicKey); + + // only one line + if (contents != null && contents.size() == 1) { + command = String.format(command, contents.get(0)); + + if (log.isInfoEnabled()) { + log.info("Add key on remote authorized keys"); + } + if (log.isDebugEnabled()) { + log.debug("command is : " + command); + } + + SSHUtils.exec(session, command); + } + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error(_("Error while uploading public key to remote serveur authorized_keys"), + e); + } + } catch (SSHException e) { + if (log.isErrorEnabled()) { + log.error(_("Error while uploading public key to remote serveur authorized_keys"), + e); + } + } + } + + /** + * Upload simulation file to server. + * + * @param session already open valid ssh session + * @param simulationFile simulation file to upload + * + * @return remote file path or <tt>null</tt> if errors + * @throws SSHException if upload fail + */ + protected String uploadSimulation(Session session, File simulationFile) throws SSHException { + + String localPath = simulationFile.getAbsolutePath(); + + // Copy simulation file in same arch as local arch + // on windows, it's a bad idee :))) + // copy it always on caparmor remote temp dir ? + // /tmp ? + // TODO check it + + String remotePath = "/tmp/"; + + if (localPath.lastIndexOf("/") > 0) { + remotePath += localPath.substring(localPath.lastIndexOf("/") + 1); + } else if (localPath.lastIndexOf("\\") > 0) { // windows + remotePath += localPath.substring(localPath.lastIndexOf("\\") + 1); + } else { + remotePath += localPath; + } + + SSHUtils.scpTo(session, simulationFile, remotePath); + + return remotePath; + } + + /** + * Download simulation results. + * + * Simulation result is save in {@link SimulationStorage#SIMULATION_PATH}. + * + * @throws SSHException if download fail (can happen if remote file doesn't exist + */ + protected void downloadResults(Session session, String simulationId) throws SSHException { + + // simulation directory + File localFile = new File(IsisFish.config.getDatabaseDirectory(), + SimulationStorage.SIMULATION_PATH); + + if (log.isDebugEnabled()) { + log.debug("Downloading results in " + localFile.getAbsolutePath()); + } + + // build remote file path + // FIXME this path should be given by remote IsisFish app + // TODO to change + String remoteFile = IsisFish.config.getSimulatorSshDataPath(); + remoteFile += "/" + SimulationStorage.SIMULATION_PATH; + remoteFile += "/" + simulationId; + + SSHUtils.scpFrom(session, remoteFile, localFile); + + } + + /* + * Launch simulation on remote server. + * + * @param session opened ssh session + * @param simulationService simulation service + * @param control control + * @param remotePath remote zip simulation path + * @throws JSchException + * + protected void launchSimulation(Session session, + SimulationService simulationService, SimulationControl control, + String remotePath) throws JSchException { + + String simulationId = control.getId(); + + String command = String.format("%s %s %s %s '%s' '%s' %s '%s' '%s'", + "java", // java command + "-jar", // java jar option + IsisFish.config.getSimulatorSshJarPath(), // remote jar path + "--option", "launch.ui", "false", // additionnal option ui + "--simulateWithSimulation", simulationId, remotePath // add option simulation + ); + + if (log.isDebugEnabled()) { + log.debug("Launch : " + command); + } + + try { + SSHUtils.exec(session, command); + } catch (SSHException e) { + if (log.isErrorEnabled()) { + log.error(_("Error while launching simulation"), e); + } + } + }*/ + + /** + * Upload script on remote server. + * + * @param session valid opened ssh session + * @param simulationScript file to upload + * + * @throws SSHException if upload fail + */ + protected String uploadSimulationScript(Session session, File simulationScript) throws SSHException { + + String localPath = simulationScript.getAbsolutePath(); + + // Copy simulation file in same arch as local arch + // on windows, it's a bad idee :))) + // copy it always on caparmor remote temp dir ? + // /tmp ? + // TODO check it + + String remotePath = "/tmp/"; + + if (localPath.lastIndexOf("/") > 0) { + remotePath += localPath.substring(localPath.lastIndexOf("/") + 1); + } else if (localPath.lastIndexOf("\\") > 0) { // windows + remotePath += localPath.substring(localPath.lastIndexOf("\\") + 1); + } else { + remotePath += localPath; + } + + SSHUtils.scpTo(session, simulationScript, remotePath); + + return remotePath; + } + + /** + * Retourne un fichier temporaire contenant le script de lancement + * de simulation. + * + * Le fichier temporaire est configuré pour se supprimer tout seul. + * + * @param simuationId id de la simulation + * @param simulationZip zip de la simulation + * + * @return un Fichier temporaire ou <tt>null</tt> en cas d'exception + * + * @throws IOException if can't build script + */ + protected File getSimulationScriptFile(String simuationId, String simulationZip) throws IOException { + + File tempScript = File.createTempFile("launch-isis", ".seq"); + tempScript.deleteOnExit(); // auto delete + + String fileContent = getSimulationScriptLaunchContent("ssh/launch-isis-pxe.seq", simuationId, simulationZip); + FileUtil.writeString(tempScript, fileContent); + + return tempScript; + } + + /** + * Utilise freemarker pour recuperer le contenu + * du script. + * + * Remplace aussi la variable $simulation du template. + * + * @param templateName url du template + * @param simuationId id de la simulation + * @param simulationZip zip de la simulation + * + * @throws IOException if can't get script content + */ + protected String getSimulationScriptLaunchContent(String templateName, + String simuationId, String simulationZip) throws IOException { + + String scriptContent = null; + + try { + // get template + Template template = freemarkerConfiguration + .getTemplate(templateName); + + // context values + Map<String, Object> root = new HashMap<String, Object>(); + root.put("simulationid", simuationId); + root.put("simulationzip", simulationZip); + + // process template + Writer out = new StringWriter(); + template.process(root, out); + out.flush(); + scriptContent = out.toString(); + + } catch (TemplateException e) { + if (log.isErrorEnabled()) { + log.error(_("Process template error"), e); + } + + throw new IOException(_("Process template error"), e); + } + + return scriptContent; + } + + /** + * Add script in remote qsub queue. + * + * @param session valid opened session + * @param scriptRemotePath remote script path + * + * @throws SSHException if call fail + */ + protected void addScriptToQsubQueue(Session session, String scriptRemotePath) throws SSHException { + + // command to : + // - add script in qsub queue + String command = "./qsub \"" + scriptRemotePath + "\""; + + int exit = SSHUtils.exec(session, command); + + if(exit != 0) { + throw new SSHException(_("Command '%s' fail to execute", command)); + } + } + + /** + * Launch a thread to check simulation progression on remote + * server. + * + * Use IsisFish.config.SIMULATOR_SSH_DATAPATH to download + * remote simulation control file. + * + * @param sshSession + * @param simulationService + * @param control + */ + protected Thread checkSimulationProgression(Session sshSession, + SimulationService simulationService, SimulationControl control) { + + Runnable controlRunnable = new RemoteSSHControlThread(sshSession, + control); + Thread controlThread = new Thread(controlRunnable); + controlThread.start(); + + return controlThread; + } + + /** + * Thread that check remote control file every 2s. + */ + protected class RemoteSSHControlThread implements Runnable { + + /** Sleep 1s */ + protected static final int SLEEPTIME = 1000; + + /** Opened ssh session */ + protected Session sshSession; + + /** Control (to update progression) */ + protected SimulationControl control; + + /** + * Constructor. + * @param sshSession + * + * @param control control + */ + protected RemoteSSHControlThread(Session sshSession, + SimulationControl control) { + this.sshSession = sshSession; + this.control = control; + } + + /* + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + + boolean running = true; + while (running) { + + // wait + try { + Thread.sleep(SLEEPTIME); + + // log (to check if thread is still alive) + if (log.isDebugEnabled()) { + log.debug("Control thread wake up"); + } + + try { + // download control file + File controlFile = downloadControlFile(control.getId()); + + if (log.isDebugEnabled()) { + log.debug("Control have been downloaded : " + + controlFile.getAbsolutePath()); + } + + synchronized (control) { + // le thread principal a pu le modifier pendant le sleep + //if (control.isRunning()) { + // on ne lit pas le stop, car le stop ne peut-etre appeler + // que par l'utilisateur qui est de ce cote de la machine + SimulationStorage.readControl(controlFile, + control, "stop"); + //} + } + + // deleteTempFile + controlFile.delete(); + } catch (SSHException e) { + // file doesn't exist + if(log.isDebugEnabled()) { + // not add ,e plz :) + log.debug(_("Remote control file doen't exists %s", e.getMessage())); + } + } + + } catch (InterruptedException e) { + if (log.isErrorEnabled()) { + log.error("Thread has been interrupted", e); + } + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Error while reading control file", e); + } + } + + synchronized (control) { + running = control.getProgressMax() == 0 || (control.getProgress() < control.getProgressMax() - 1); + } + } + } + + /** + * Download remote simulation control file and store + * its content into temp file. + * + * @return downloaded temp file (file have to be manually deleted) + * @throws IOException + * @throws SSHException if remote file doesn't exists + */ + protected File downloadControlFile(String simulationId) + throws IOException, SSHException { + + File localFile = null; + + // build remote file path + // FIXME this path should be given by remote IsisFish app + // TODO to change + String remoteFile = IsisFish.config.getSimulatorSshDataPath(); + remoteFile += "/" + SimulationStorage.SIMULATION_PATH; + remoteFile += "/" + simulationId; + remoteFile += "/control"; + + // local tmp file + localFile = File.createTempFile(simulationId, "control"); + + SSHUtils.scpFrom(sshSession, remoteFile, localFile); + + return localFile; + } + } +} Property changes on: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHSimulatorLauncher.java ___________________________________________________________________ Name: svn:mergeinfo + Deleted: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHUtils.java =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHUtils.java 2009-01-23 09:22:36 UTC (rev 1753) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHUtils.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -1,495 +0,0 @@ -/* *##% - * Copyright (C) 2008 Code Lutin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - *##%*/ - -package fr.ifremer.isisfish.simulator.launcher; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.jcraft.jsch.Channel; -import com.jcraft.jsch.ChannelExec; -import com.jcraft.jsch.JSchException; -import com.jcraft.jsch.Session; - -/** - * SSH utils class. - * - * All this code has be taken from ant optionnal ssh task. - * - * Use full for: - * - scpTo command - * - scpFrom command - * - exec command - * - * @author chatellier - * @version $Revision: 1.0 $ - * - * Last update : $Date: 13 janv. 2009 $ - * By : $Author: chatellier $ - */ -public class SSHUtils { - - /** log. */ - private static Log log = LogFactory.getLog(SSHUtils.class); - - protected static final byte LINE_FEED = 0x0a; - protected static final int BUFFER_SIZE = 1024; - private static final int HUNDRED_KILOBYTES = 102400; - - /** Utility class */ - protected SSHUtils() { - - } - - /** - * Exec command on remote server. - * - * @param session opened valid session - * @param command command to exec - * @throws SSHException - */ - public static void exec(Session session, String command) - throws SSHException { - - try { - // exec previous command - Channel channel = session.openChannel("exec"); - ((ChannelExec) channel).setCommand(command); - - BufferedReader br = new BufferedReader(new InputStreamReader( - channel.getInputStream())); - channel.connect(); - String line = null; - while (true) { - while ((line = br.readLine()) != null) { - if (log.isInfoEnabled()) { - log.info("Remote output : " + line); - } - } - if (channel.isClosed()) { - if (log.isInfoEnabled()) { - log.info("JSch channel exit-status: " - + channel.getExitStatus()); - } - break; - } - try { - Thread.sleep(500); - } catch (Exception ee) { - } - } - channel.disconnect(); - // end read buffer - } catch (JSchException e) { - throw new SSHException("I/O error while executing command", e); - } catch (IOException e) { - throw new SSHException("I/O error while executing command", e); - } - - } - - /** - * Download a local file from remote server. - * - * @param session opened valid jsch session - * @param remoteFileName remote file name to download - * @param localFile local file name to download into - * - * @throws SSHException - */ - public static void scpFrom(Session session, String remoteFileName, - File localFile) throws SSHException { - - String command = "scp -f -r \"" + remoteFileName + "\""; - - ChannelExec channel = null; - try { - channel = (ChannelExec) session.openChannel("exec"); - channel.setCommand(command); - - // get I/O streams for remote scp - OutputStream out = channel.getOutputStream(); - InputStream in = channel.getInputStream(); - - channel.connect(); - - sendAck(out); - startRemoteCpProtocol(in, out, localFile); - } catch (IOException e) { - throw new SSHException(e); - } catch (JSchException e) { - throw new SSHException(e); - } finally { - if (channel != null) { - channel.disconnect(); - } - } - } - - /** - * Upload file on remote server. - * - * @param session opened valid session - * @param localFile file to upload - * @param remoteFilePath remote file path - * - * @throws SSHException - */ - public static void scpTo(Session session, File localFile, - String remoteFilePath) throws SSHException { - - try { - doSingleTransfer(session, localFile, remoteFilePath); - } catch (IOException e) { - throw new SSHException(e); - } catch (JSchException e) { - throw new SSHException(e); - } - } - - /** - * Send an ack. - * @param out the output stream to use - * @throws IOException on error - */ - protected static void sendAck(OutputStream out) throws IOException { - byte[] buf = new byte[1]; - buf[0] = 0; - out.write(buf); - out.flush(); - } - - /** - * Reads the response, throws a BuildException if the response - * indicates an error. - * @param in the input stream to use - * @throws IOException on I/O error - */ - protected static void waitForAck(InputStream in) throws IOException, - SSHException { - int b = in.read(); - - // b may be 0 for success, - // 1 for error, - // 2 for fatal error, - - if (b == -1) { - // didn't receive any response - throw new SSHException("No response from server"); - } else if (b != 0) { - StringBuffer sb = new StringBuffer(); - - int c = in.read(); - while (c > 0 && c != '\n') { - sb.append((char) c); - c = in.read(); - } - - if (b == 1) { - throw new SSHException("server indicated an error: " - + sb.toString()); - } else if (b == 2) { - throw new SSHException("server indicated a fatal error: " - + sb.toString()); - } else { - throw new SSHException("unknown response, code " + b - + " message: " + sb.toString()); - } - } - } - - /** - * Track progress every 10% if 100kb < filesize < 1mb. For larger - * files track progress for every percent transmitted. - * @param filesize the size of the file been transmitted - * @param totalLength the total transmission size - * @param percentTransmitted the current percent transmitted - * @return the percent that the file is of the total - */ - protected static int trackProgress(long filesize, long totalLength, - int percentTransmitted) { - - // CheckStyle:MagicNumber OFF - int percent = (int) Math.round(Math - .floor((totalLength / (double) filesize) * 100)); - - if (percent > percentTransmitted) { - if (filesize < 1048576) { - if (percent % 5 == 0) { - if (percent == 100) { - System.out.println(" 100%"); - } else { - System.out.print("*"); - } - } - } else { - if (percent == 50) { - System.out.println(" 50%"); - } else if (percent == 100) { - System.out.println(" 100%"); - } else { - System.out.print("."); - } - } - } - // CheckStyle:MagicNumber ON - - return percent; - } - - protected static void startRemoteCpProtocol(InputStream in, - OutputStream out, File localFile) throws IOException, SSHException { - File startFile = localFile; - while (true) { - // C0644 filesize filename - header for a regular file - // T time 0 time 0\n - present if perserve time. - // D directory - this is the header for a directory. - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - while (true) { - int read = in.read(); - if (read < 0) { - return; - } - if ((byte) read == LINE_FEED) { - break; - } - stream.write(read); - } - String serverResponse = stream.toString("UTF-8"); - if (serverResponse.charAt(0) == 'C') { - parseAndFetchFile(serverResponse, startFile, out, in); - } else if (serverResponse.charAt(0) == 'D') { - startFile = parseAndCreateDirectory(serverResponse, startFile); - sendAck(out); - } else if (serverResponse.charAt(0) == 'E') { - startFile = startFile.getParentFile(); - sendAck(out); - } else if (serverResponse.charAt(0) == '\01' - || serverResponse.charAt(0) == '\02') { - // this indicates an error. - throw new IOException(serverResponse.substring(1)); - } - } - } - - protected static File parseAndCreateDirectory(String serverResponse, - File localFile) { - int start = serverResponse.indexOf(" "); - // appears that the next token is not used and it's zero. - start = serverResponse.indexOf(" ", start + 1); - String directoryName = serverResponse.substring(start + 1); - if (localFile.isDirectory()) { - File dir = new File(localFile, directoryName); - dir.mkdir(); - log.debug("Creating: " + dir); - return dir; - } - return null; - } - - protected static void parseAndFetchFile(String serverResponse, - File localFile, OutputStream out, InputStream in) - throws IOException, SSHException { - int start = 0; - int end = serverResponse.indexOf(" ", start + 1); - start = end + 1; - end = serverResponse.indexOf(" ", start + 1); - long filesize = Long.parseLong(serverResponse.substring(start, end)); - String filename = serverResponse.substring(end + 1); - log.debug("Receiving: " + filename + " : " + filesize); - File transferFile = (localFile.isDirectory()) ? new File(localFile, - filename) : localFile; - fetchFile(transferFile, filesize, out, in); - waitForAck(in); - sendAck(out); - } - - protected static void fetchFile(File localFile, long filesize, - OutputStream out, InputStream in) throws IOException { - byte[] buf = new byte[BUFFER_SIZE]; - sendAck(out); - - // read a content of lfile - FileOutputStream fos = new FileOutputStream(localFile); - int length; - long totalLength = 0; - - // only track progress for files larger than 100kb in verbose mode - boolean trackProgress = filesize > HUNDRED_KILOBYTES; - // since filesize keeps on decreasing we have to store the - // initial filesize - long initFilesize = filesize; - int percentTransmitted = 0; - - try { - while (true) { - length = in.read(buf, 0, (BUFFER_SIZE < filesize) ? BUFFER_SIZE - : (int) filesize); - if (length < 0) { - throw new EOFException("Unexpected end of stream."); - } - fos.write(buf, 0, length); - filesize -= length; - totalLength += length; - if (filesize == 0) { - break; - } - - if (trackProgress) { - percentTransmitted = trackProgress(initFilesize, - totalLength, percentTransmitted); - } - } - } finally { - fos.flush(); - fos.close(); - } - } - - protected static void doSingleTransfer(Session session, File localFile, - String remoteFilePath) throws IOException, JSchException, - SSHException { - - String command = "scp -t \"" + remoteFilePath + "\""; - ChannelExec channel = (ChannelExec) session.openChannel("exec"); - channel.setCommand(command); - try { - - OutputStream out = channel.getOutputStream(); - InputStream in = channel.getInputStream(); - - channel.connect(); - - waitForAck(in); - sendFileToRemote(localFile, in, out); - } finally { - channel.disconnect(); - } - } - - protected static void sendFileToRemote(File localFile, InputStream in, - OutputStream out) throws IOException, SSHException { - // send "C0644 filesize filename", where filename should not include '/' - long filesize = localFile.length(); - String command = "C0644 " + filesize + " "; - command += localFile.getName(); - command += "\n"; - - out.write(command.getBytes()); - out.flush(); - - waitForAck(in); - - // send a content of lfile - FileInputStream fis = new FileInputStream(localFile); - byte[] buf = new byte[BUFFER_SIZE]; - long totalLength = 0; - - // only track progress for files larger than 100kb in verbose mode - boolean trackProgress = filesize > HUNDRED_KILOBYTES; - // since filesize keeps on decreasing we have to store the - // initial filesize - long initFilesize = filesize; - int percentTransmitted = 0; - - try { - while (true) { - int len = fis.read(buf, 0, buf.length); - if (len <= 0) { - break; - } - out.write(buf, 0, len); - totalLength += len; - - if (trackProgress) { - percentTransmitted = trackProgress(initFilesize, - totalLength, percentTransmitted); - } - } - out.flush(); - sendAck(out); - waitForAck(in); - } finally { - fis.close(); - } - } -} - -/** - * SSHException. - * - * @author chatellier - * @version $Revision: 1.0 $ - * - * Last update : $Date: 14 janv. 2009 $ - * By : $Author: chatellier $ - */ -class SSHException extends Exception { - - /** serialVersionUID. */ - private static final long serialVersionUID = -198651402309210758L; - - /** - * Constructs a new exception with null as its detail message. - */ - public SSHException() { - super(); - } - - /** - * Constructs a new exception with the specified detail message. - * - * @param message message - * @param cause cause - */ - public SSHException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructs a new exception with the specified detail message and cause. - * - * @param message message - */ - public SSHException(String message) { - super(message); - } - - /** - * Constructs a new exception with the specified cause and a detail message - * of (cause==null ? null : cause.toString()) (which typically contains the - * class and detail message of cause). - * - * @param cause cause - */ - public SSHException(Throwable cause) { - super(cause); - } - -} \ No newline at end of file Modified: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SimulationJob.java =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SimulationJob.java 2009-01-23 09:22:36 UTC (rev 1753) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SimulationJob.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -238,7 +238,9 @@ log.error(_("Can't do post action %s", action), eee); } } - // la simulation est termine on, avant la progress au dernier cran + // la simulation est termine on avance la progress au dernier cran + // attention on utilise ca aussi pour detecer la fin d'une simulation + // quand date =progressMax control.setProgress(control.getProgress()+1); } Modified: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SimulatorLauncher.java =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SimulatorLauncher.java 2009-01-23 09:22:36 UTC (rev 1753) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SimulatorLauncher.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2008 Code Lutin, Benjamin Poussin + * Copyright (C) 2002-2009 Code Lutin, Benjamin Poussin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,7 +50,7 @@ * responsable du stockage de chaque simulation dans des storages. * <b>IMPORTANT</b> Quoi qu'il arrive * - * @throws java.rmi.RemoteException Si pour l'execution de la simulation + * @throws RemoteException Si pour l'execution de la simulation * on utilise des resources distantes (serveurs) et que ceux si non pas pu * etre contacte. Dans ce cas la simulation n'a pas ete faite et cette * exception est levee. Deleted: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SshSimulatorLauncher.java =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SshSimulatorLauncher.java 2009-01-23 09:22:36 UTC (rev 1753) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SshSimulatorLauncher.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -1,679 +0,0 @@ -/* *##% - * Copyright (C) 2008 Code Lutin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - *##%*/ - -package fr.ifremer.isisfish.simulator.launcher; - -import static org.codelutin.i18n.I18n._; - -import java.awt.Container; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.io.File; -import java.io.IOException; -import java.rmi.RemoteException; -import java.util.List; - -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPasswordField; -import javax.swing.JTextField; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.jcraft.jsch.JSch; -import com.jcraft.jsch.JSchException; -import com.jcraft.jsch.Session; -import com.jcraft.jsch.UIKeyboardInteractive; -import com.jcraft.jsch.UserInfo; - -import fr.ifremer.isisfish.IsisFish; -import fr.ifremer.isisfish.datastore.SimulationStorage; -import fr.ifremer.isisfish.simulator.SimulationControl; -import fr.ifremer.isisfish.simulator.SimulationException; - -/** - * Use a remote simulation server. - * - * Upload zip simulation file on server and launch - * simulation on that file. - * - * Isis-Fish must be installed on remote server. - * - * @see JSch - * - * @author chatellier - * @version $Revision: 1.0 $ - * - * Last update : $Date: 18 nov. 2008 $ - * By : $Author: chatellier $ - */ -public class SshSimulatorLauncher implements SimulatorLauncher { - - /** Class logger */ - protected static Log log = LogFactory.getLog(SshSimulatorLauncher.class); - - /** - * Display message both in commons-logging and control text progress. - * - * @param control control - * @param message message to display - */ - protected void message(SimulationControl control, String message) { - // log - if (log.isInfoEnabled()) { - log.info(message); - } - // control - if (control != null) { - control.setText(message); - } - } - - /* - * @see fr.ifremer.isisfish.simulator.launcher.SimulatorLauncher#maxSimulationThread() - */ - @Override - public int maxSimulationThread() { - return 1; - } - - /* - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return _("isisfish.simulator.launcher.remote"); - } - - /* - * @see fr.ifremer.isisfish.simulator.launcher.SimulatorLauncher#simulate(fr.ifremer.isisfish.simulator.launcher.SimulationService, fr.ifremer.isisfish.simulator.SimulationControl, java.io.File) - */ - @Override - public SimulationStorage simulate(SimulationService simulationService, - SimulationControl control, File simulationZip) - throws RemoteException { - - // start ssh session - try { - - // connection - message(control, _("isisfish.simulation.remote.message.connection")); - Session sshSession = openSSHSession(); - - // upload simulation on server - message(control, _("isisfish.simulation.remote.message.upload")); - String remotePath = uploadSimulation(sshSession, simulationZip); - - // si l'upload a fonctionné - if (remotePath != null) { - message(control, _("isisfish.simulation.remote.message.launch")); - - // just start a thread - checkSimulationProgression(sshSession, simulationService, - control); - launchSimulation(sshSession, simulationService, control, - remotePath); - - // recuperation des resultats - message(control, _("isisfish.simulation.remote.message.downloadresults")); - downloadResults(sshSession, control.getId()); - - // force thread to stop - synchronized(control) { - control.stopSimulation(); - } - } - } catch (JSchException e) { - if (log.isErrorEnabled()) { - log.error(_("isisfish.error.simulation.remote.global")); - } - throw new SimulationException( - _("isisfish.error.simulation.remote.global"), e); - } - - return null; - } - - /** - * Connect to remote server throw SSH, and return session. - * - * @return valid opened session - * - * @throws JSchException - */ - protected Session openSSHSession() throws JSchException { - - JSch jsch = new JSch(); - - // extract connection infos - String host = IsisFish.config.getSimulatorSshServer(); - String username = IsisFish.config.getSimulatorSshUsername(); - - int port = 22; // by default, 22 - - if (host.indexOf(':') > 0) { - String sPort = host.substring(host.indexOf(':') + 1); - try { - port = Integer.parseInt(sPort); - } catch (NumberFormatException e) { - if (log.isWarnEnabled()) { - log.warn(_( - "isisfish.error.simulation.remote.wrongportvalue", - sPort)); - } - } - host = host.substring(0, host.indexOf(':')); - } - - if (log.isInfoEnabled()) { - log.info(_("Try to log on %s@%s:%d", username, host, port)); - } - - // add ssh key - boolean sshKeyUsed = false; - File sshKey = IsisFish.config.getSSHPrivateKeyFilePath(); - if (sshKey.canRead()) { - if (log.isInfoEnabled()) { - log.info(_("Ssh key found '%s' will be used to connect to", - sshKey.getAbsoluteFile(), host)); - } - jsch.addIdentity(sshKey.getAbsolutePath()); - sshKeyUsed = true; - } - - Session session = jsch.getSession(username, host, port); - - // username and password will be given via UserInfo interface. - UserInfo ui = new SSHUserInfo(); - session.setUserInfo(ui); - session.connect(60000); // timeout - - // test here, if password has been asked to user - if (session.isConnected() && sshKeyUsed && ui.getPassword() != null) { - putSshKeyOnRemoteServer(session, sshKey); - } - return session; - } - - /** - * Close ssh session. - * - * @param session session to close - */ - protected void closeSSHSession(Session session) { - if (session != null) { - session.disconnect(); - } - } - - /** - * Add ssh key into $HOME/.ssh/authorized_keys file. - * - * Just connect and do an "echo xx >> - * @param session opened session - * @param sshKey - * @throws JSchException - */ - protected void putSshKeyOnRemoteServer(Session session, File sshKey) - throws JSchException { - - // get public key for argument private key file - // TODO always .pub ? - File publicKey = new File(sshKey.getAbsoluteFile() + ".pub"); - - // command to : - // - make ssh directory - // - add key to authorized_keys - String command = "if [ ! -d .ssh ] ; then mkdir .ssh; fi ; echo \"%s\" >> .ssh/authorized_keys"; - - try { - // use usefull readLines from commons-io - @SuppressWarnings("unchecked") - List<String> contents = FileUtils.readLines(publicKey); - - // only one line - if (contents != null && contents.size() == 1) { - command = String.format(command, contents.get(0)); - - if (log.isInfoEnabled()) { - log.info("Add key on remote authorized keys"); - } - if (log.isDebugEnabled()) { - log.debug("command is : " + command); - } - - SSHUtils.exec(session, command); - } - } catch (IOException e) { - if (log.isErrorEnabled()) { - log.error(_("Error while uploading public key to remote serveur authorized_keys"), - e); - } - } - catch (SSHException e) { - if (log.isErrorEnabled()) { - log.error(_("Error while uploading public key to remote serveur authorized_keys"), - e); - } - } - } - - /** - * Upload simulation file to server. - * - * @param session already open valid ssh session - * @param simulationFile simulation file to upload - * - * @return remote file path or <tt>null</tt> if errors - */ - protected String uploadSimulation(Session session, File simulationFile) { - - String localPath = simulationFile.getAbsolutePath(); - - // Copy simulation file in same arch as local arch - // on windows, it's a bad idee :))) - // copy it always on caparmor remote temp dir ? - // /tmp ? - // TODO check it - - String remotePath = "/tmp/"; - - if(localPath.lastIndexOf("/") > 0) { - remotePath += localPath.substring(localPath.lastIndexOf("/") + 1); - } - else if(localPath.lastIndexOf("\\") > 0) { // windows - remotePath += localPath.substring(localPath.lastIndexOf("\\") + 1); - } - else { - remotePath += localPath; - } - - try { - SSHUtils.scpTo(session, simulationFile, remotePath); - } - catch(SSHException e) { - if (log.isErrorEnabled()) { - log.error(_("Error while uploading simulation"), - e); - } - - remotePath = null; - } - - return remotePath; - } - - /** - * Download resulation results. - * - * @return downloaded temp file (file have to be manually deleted) - */ - protected File downloadResults(Session session, String simulationId) { - - // simulation directory - File localFile = new File(IsisFish.config.getDatabaseDirectory(), - SimulationStorage.SIMULATION_PATH); - - if(log.isDebugEnabled()) { - log.debug("Downloading results in " + localFile.getAbsolutePath()); - } - - // build remote file path - // FIXME this path should be given by remote IsisFish app - // TODO to change - String remoteFile = IsisFish.config.getSimulatorSshDataPath(); - remoteFile += "/" + SimulationStorage.SIMULATION_PATH; - remoteFile += "/" + simulationId; - - try { - SSHUtils.scpFrom(session, remoteFile, localFile); - } - catch(SSHException e) { - // error can append because control file does'nt exist yet - if (log.isDebugEnabled()) { - log.debug(_("Error while downloading simulation control")); - } - - localFile = null; - } - - return localFile; - } - - /** - * Launch simulation on remote server. - * - * @param session opened ssh session - * @param simulationService simulation service - * @param control control - * @param remotePath remote zip simulation path - * @throws JSchException - */ - protected void launchSimulation(Session session, - SimulationService simulationService, SimulationControl control, - String remotePath) throws JSchException { - - String simulationId = control.getId(); - - String command = String.format("%s %s %s %s '%s' '%s' %s '%s' '%s'", - "java", // java command - "-jar", // java jar option - IsisFish.config.getSimulatorSshJarPath(), // remote jar path - "--option", "launch.ui", "false", // additionnal option ui - "--simulateWithSimulation", simulationId, remotePath // add option simulation - ); - - if (log.isDebugEnabled()) { - log.debug("Launch : " + command); - } - - try { - SSHUtils.exec(session, command); - } - catch(SSHException e) { - if (log.isErrorEnabled()) { - log.error(_("Error while launching simulation"), - e); - } - } - } - - /** - * Launch a thread to check simulation progression on remote - * server. - * - * Use IsisFish.config.SIMULATOR_SSH_DATAPATH to download - * remote simulation control file. - * - * @param sshSession - * @param simulationService - * @param control - */ - protected void checkSimulationProgression(Session sshSession, - SimulationService simulationService, SimulationControl control) { - - Runnable controlRunnable = new RemoteSSHControlThread(sshSession, control); - Thread controlThread = new Thread(controlRunnable); - controlThread.start(); - - } - - /** - * Thread that check remote control file every 2s. - */ - protected class RemoteSSHControlThread implements Runnable { - - /** Sleep 1s */ - protected static final int SLEEPTIME = 1000; - - /** Opened ssh session */ - protected Session sshSession; - - /** Control (to update progression) */ - protected SimulationControl control; - - /** - * Constructor. - * @param sshSession - * - * @param control control - */ - protected RemoteSSHControlThread(Session sshSession, SimulationControl control) { - this.sshSession = sshSession; - this.control = control; - } - - /* - * @see java.lang.Runnable#run() - */ - @Override - public void run() { - - boolean running = true; - while (running) { - - // wait - try { - Thread.sleep(SLEEPTIME); - - // download control file - File controlFile = downloadControlFile(control.getId()); - - if (controlFile != null) { - if(log.isDebugEnabled()) { - log.debug("Control have been downloaded : " + controlFile.getAbsolutePath()); - } - - synchronized (control) { - // le thread principal a pu le modifier pendant le sleep - if(control.isRunning()) { - // on ne lit pas le stop, car le stop ne peut-etre appeler - // que par l'utilisateur qui est de ce cote de la machine - SimulationStorage.readControl(controlFile, control, - "stop"); - } - } - - // deleteTempFile - controlFile.delete(); - } - else { - if(log.isDebugEnabled()) { - log.debug("Control can't have been downloaded"); - } - } - - } catch (InterruptedException e) { - if (log.isErrorEnabled()) { - log.error("Thread has been interrupted", e); - } - } catch (IOException e) { - if (log.isErrorEnabled()) { - log.error("Error while reading control file", e); - } - } - - synchronized (control) { - running = control.isRunning(); - } - } - } - - /** - * Download remote simulation control file and store - * its content into temp file. - * - * @return downloaded temp file (file have to be manually deleted) - * @throws IOException - */ - protected File downloadControlFile(String simulationId) - throws IOException { - - File localFile = null; - - // build remote file path - // FIXME this path should be given by remote IsisFish app - // TODO to change - String remoteFile = IsisFish.config.getSimulatorSshDataPath(); - remoteFile += "/" + SimulationStorage.SIMULATION_PATH; - remoteFile += "/" + simulationId; - remoteFile += "/control"; - - // local tmp file - localFile = File.createTempFile(simulationId, "control"); - - try { - SSHUtils.scpFrom(sshSession, remoteFile, localFile); - } - catch(SSHException e) { - if (log.isErrorEnabled()) { - log.error(_("Error while launching simulation"), - e); - } - - localFile = null; - } - return localFile; - } - } -} - -/** - * Class used to ask used for connection info. - * - * Password, passphrase... - */ -class SSHUserInfo implements UserInfo, UIKeyboardInteractive { - - /** - * Passphrase. - * - * Static to be stored on multiple connexion. - */ - protected static String passphrase; - - /** - * Passphrase text field. - */ - protected JTextField passphraseField = new JPasswordField(20); - - /** - * Password text field. - */ - protected String passwd; - - /** - * Password text field. - */ - protected JTextField passwordField = new JPasswordField(20); - - /** - * Call to ask user in remote server key - * can be trusted. Here, auto accept. - */ - @Override - public boolean promptYesNo(String str) { - return true; - } - - @Override - public String getPassphrase() { - return passphrase; - } - - @Override - public boolean promptPassphrase(String message) { - Object[] ob = { passphraseField }; - int result = JOptionPane.showConfirmDialog(null, ob, message, - JOptionPane.OK_CANCEL_OPTION); - boolean bResult = false; - if (result == JOptionPane.OK_OPTION) { - passphrase = passphraseField.getText(); - bResult = true; - } - return bResult; - } - - @Override - public String getPassword() { - return passwd; - } - - @Override - public boolean promptPassword(String message) { - Object[] ob = { passwordField }; - int result = JOptionPane.showConfirmDialog(null, ob, message, - JOptionPane.OK_CANCEL_OPTION); - boolean bResult = false; - if (result == JOptionPane.OK_OPTION) { - passwd = passwordField.getText(); - bResult = true; - } - return bResult; - } - - @Override - public void showMessage(String message) { - JOptionPane.showMessageDialog(null, message); - } - - @Override - public String[] promptKeyboardInteractive(String destination, String name, - String instruction, String[] prompt, boolean[] echo) { - final GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 1, 1, - GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, - new Insets(0, 0, 0, 0), 0, 0); - Container panel = new JPanel(); - panel.setLayout(new GridBagLayout()); - - gbc.weightx = 1.0; - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.gridx = 0; - panel.add(new JLabel(instruction), gbc); - gbc.gridy++; - - gbc.gridwidth = GridBagConstraints.RELATIVE; - - JTextField[] texts = new JTextField[prompt.length]; - for (int i = 0; i < prompt.length; i++) { - gbc.fill = GridBagConstraints.NONE; - gbc.gridx = 0; - gbc.weightx = 1; - panel.add(new JLabel(prompt[i]), gbc); - - gbc.gridx = 1; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.weighty = 1; - if (echo[i]) { - texts[i] = new JTextField(20); - } else { - texts[i] = new JPasswordField(20); - } - panel.add(texts[i], gbc); - gbc.gridy++; - } - - String[] response = null; - if (JOptionPane.showConfirmDialog(null, panel, destination + ": " - + name, JOptionPane.OK_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) { - response = new String[prompt.length]; - for (int i = 0; i < prompt.length; i++) { - response[i] = texts[i].getText(); - - // hack to remember password - if (prompt[i] != null - && prompt[i].toLowerCase().indexOf("password") >= 0) { - passwd = response[i]; - } - - // hack to remember passphrase - if (prompt[i] != null - && prompt[i].toLowerCase().indexOf("passphrase") >= 0) { - passphrase = response[i]; - } - } - } - // else = cancel - return response; - } -} Added: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/ClasspathTemplateLoader.java =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/ClasspathTemplateLoader.java (rev 0) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/ClasspathTemplateLoader.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -0,0 +1,68 @@ +/* *##% + * Copyright (C) 2008, 2009 Code Lutin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +package fr.ifremer.isisfish.simulator.launcher.ssh; + +import java.net.URL; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codelutin.util.Resource; +import org.codelutin.util.ResourceNotFoundException; + +import freemarker.cache.URLTemplateLoader; + +/** + * Freemarker custom template loader. + * Load template from classpath jars. + * + * @author chatellier + * @version $Revision: 1.0 $ + * + * Last update : $Date: 1 déc. 2008 $ + * By : $Author: chatellier $ + */ +public class ClasspathTemplateLoader extends URLTemplateLoader { + + /** log */ + private static Log log = LogFactory.getLog(ClasspathTemplateLoader.class); + + /* + * @see freemarker.cache.URLTemplateLoader#getURL(java.lang.String) + */ + @Override + protected URL getURL(String name) { + + if (log.isDebugEnabled()) { + log.debug("Loading url template " + name); + } + + URL url = null; + + try { + url = Resource.getURL(name); + } catch (ResourceNotFoundException e) { + // not big deal + // freemarker ask for name_fr_FR, name_fr, name + url = null; + } + + return url; + } + +} Added: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHException.java =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHException.java (rev 0) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHException.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -0,0 +1,72 @@ +/* *##% + * Copyright (C) 2008, 2009 Code Lutin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +package fr.ifremer.isisfish.simulator.launcher.ssh; + +/** + * SSHException. + * + * @author chatellier + * @version $Revision: 1.0 $ + * + * Last update : $Date: 14 janv. 2009 $ + * By : $Author: chatellier $ + */ +public class SSHException extends Exception { + + /** serialVersionUID. */ + private static final long serialVersionUID = -198651402309210758L; + + /** + * Constructs a new exception with null as its detail message. + */ + public SSHException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. + * + * @param message message + * @param cause cause + */ + public SSHException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * + * @param message message + */ + public SSHException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified cause and a detail message + * of (cause==null ? null : cause.toString()) (which typically contains the + * class and detail message of cause). + * + * @param cause cause + */ + public SSHException(Throwable cause) { + super(cause); + } + +} Added: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHUserInfo.java =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHUserInfo.java (rev 0) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHUserInfo.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -0,0 +1,180 @@ +/* *##% + * Copyright (C) 2008, 2009 Code Lutin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +package fr.ifremer.isisfish.simulator.launcher.ssh; + +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; + +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; + +/** + * Class used to ask used for connection info. + * + * Password, passphrase... + * + * @author chatellier + * @version $Revision: 1.0 $ + * + * Last update : $Date: 1 déc. 2008 $ + * By : $Author: chatellier $ + */ +public class SSHUserInfo implements UserInfo, UIKeyboardInteractive { + + /** + * Passphrase. + * + * Static to be stored on multiple connexion. + */ + protected static String passphrase; + + /** + * Passphrase text field. + */ + protected JTextField passphraseField = new JPasswordField(20); + + /** + * Password text field. + */ + protected String passwd; + + /** + * Password text field. + */ + protected JTextField passwordField = new JPasswordField(20); + + /** + * Call to ask user in remote server key + * can be trusted. Here, auto accept. + */ + @Override + public boolean promptYesNo(String str) { + return true; + } + + @Override + public String getPassphrase() { + return passphrase; + } + + @Override + public boolean promptPassphrase(String message) { + Object[] ob = { passphraseField }; + int result = JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + boolean bResult = false; + if (result == JOptionPane.OK_OPTION) { + passphrase = passphraseField.getText(); + bResult = true; + } + return bResult; + } + + @Override + public String getPassword() { + return passwd; + } + + @Override + public boolean promptPassword(String message) { + Object[] ob = { passwordField }; + int result = JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + boolean bResult = false; + if (result == JOptionPane.OK_OPTION) { + passwd = passwordField.getText(); + bResult = true; + } + return bResult; + } + + @Override + public void showMessage(String message) { + JOptionPane.showMessageDialog(null, message); + } + + @Override + public String[] promptKeyboardInteractive(String destination, String name, + String instruction, String[] prompt, boolean[] echo) { + final GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 1, 1, + GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0); + Container panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts = new JTextField[prompt.length]; + for (int i = 0; i < prompt.length; i++) { + gbc.fill = GridBagConstraints.NONE; + gbc.gridx = 0; + gbc.weightx = 1; + panel.add(new JLabel(prompt[i]), gbc); + + gbc.gridx = 1; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weighty = 1; + if (echo[i]) { + texts[i] = new JTextField(20); + } else { + texts[i] = new JPasswordField(20); + } + panel.add(texts[i], gbc); + gbc.gridy++; + } + + String[] response = null; + if (JOptionPane.showConfirmDialog(null, panel, destination + ": " + + name, JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) { + response = new String[prompt.length]; + for (int i = 0; i < prompt.length; i++) { + response[i] = texts[i].getText(); + + // hack to remember password + if (prompt[i] != null + && prompt[i].toLowerCase().indexOf("password") >= 0) { + passwd = response[i]; + } + + // hack to remember passphrase + if (prompt[i] != null + && prompt[i].toLowerCase().indexOf("passphrase") >= 0) { + passphrase = response[i]; + } + } + } + // else = cancel + return response; + } +} Copied: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHUtils.java (from rev 1751, isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/SSHUtils.java) =================================================================== --- isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHUtils.java (rev 0) +++ isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHUtils.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -0,0 +1,448 @@ +/* *##% + * Copyright (C) 2008, 2009 Code Lutin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +package fr.ifremer.isisfish.simulator.launcher.ssh; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +/** + * SSH utils class. + * + * All this code has be taken from ant optionnal ssh task. + * + * Use full for: + * - scpTo command + * - scpFrom command + * - exec command + * + * @author chatellier + * @version $Revision: 1.0 $ + * + * Last update : $Date: 13 janv. 2009 $ + * By : $Author: chatellier $ + */ +public class SSHUtils { + + /** log. */ + private static Log log = LogFactory.getLog(SSHUtils.class); + + protected static final byte LINE_FEED = 0x0a; + protected static final int BUFFER_SIZE = 1024; + private static final int HUNDRED_KILOBYTES = 102400; + + /** Utility class */ + protected SSHUtils() { + + } + + /** + * Exec command on remote server. + * + * @param session opened valid session + * @param command command to exec + * + * @return channel exit status (0 = no problem) + * @throws SSHException + */ + public static int exec(Session session, String command) + throws SSHException { + + int exitStatus = 0; + + try { + // exec previous command + Channel channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand(command); + + BufferedReader br = new BufferedReader(new InputStreamReader( + channel.getInputStream())); + channel.connect(); + String line = null; + while (true) { + while ((line = br.readLine()) != null) { + if (log.isInfoEnabled()) { + log.info("Remote output : " + line); + } + } + if (channel.isClosed()) { + exitStatus = channel.getExitStatus(); + if (log.isInfoEnabled()) { + log.info("JSch channel exit-status: " + + exitStatus); + } + break; + } + try { + Thread.sleep(500); + } catch (Exception ee) { + } + } + channel.disconnect(); + // end read buffer + } catch (JSchException e) { + throw new SSHException("I/O error while executing command", e); + } catch (IOException e) { + throw new SSHException("I/O error while executing command", e); + } + + return exitStatus; + } + + /** + * Download a local file from remote server. + * + * @param session opened valid jsch session + * @param remoteFileName remote file name to download + * @param localFile local file name to download into + * + * @throws SSHException + */ + public static void scpFrom(Session session, String remoteFileName, + File localFile) throws SSHException { + + String command = "scp -f -r \"" + remoteFileName + "\""; + + ChannelExec channel = null; + try { + channel = (ChannelExec) session.openChannel("exec"); + channel.setCommand(command); + + // get I/O streams for remote scp + OutputStream out = channel.getOutputStream(); + InputStream in = channel.getInputStream(); + + channel.connect(); + + sendAck(out); + startRemoteCpProtocol(in, out, localFile); + } catch (IOException e) { + throw new SSHException(e); + } catch (JSchException e) { + throw new SSHException(e); + } finally { + if (channel != null) { + channel.disconnect(); + } + } + } + + /** + * Upload file on remote server. + * + * @param session opened valid session + * @param localFile file to upload + * @param remoteFilePath remote file path + * + * @throws SSHException + */ + public static void scpTo(Session session, File localFile, + String remoteFilePath) throws SSHException { + + try { + doSingleTransfer(session, localFile, remoteFilePath); + } catch (IOException e) { + throw new SSHException(e); + } catch (JSchException e) { + throw new SSHException(e); + } + } + + /** + * Send an ack. + * @param out the output stream to use + * @throws IOException on error + */ + protected static void sendAck(OutputStream out) throws IOException { + byte[] buf = new byte[1]; + buf[0] = 0; + out.write(buf); + out.flush(); + } + + /** + * Reads the response, throws a BuildException if the response + * indicates an error. + * @param in the input stream to use + * @throws IOException on I/O error + */ + protected static void waitForAck(InputStream in) throws IOException, + SSHException { + int b = in.read(); + + // b may be 0 for success, + // 1 for error, + // 2 for fatal error, + + if (b == -1) { + // didn't receive any response + throw new SSHException("No response from server"); + } else if (b != 0) { + StringBuffer sb = new StringBuffer(); + + int c = in.read(); + while (c > 0 && c != '\n') { + sb.append((char) c); + c = in.read(); + } + + if (b == 1) { + throw new SSHException("server indicated an error: " + + sb.toString()); + } else if (b == 2) { + throw new SSHException("server indicated a fatal error: " + + sb.toString()); + } else { + throw new SSHException("unknown response, code " + b + + " message: " + sb.toString()); + } + } + } + + /** + * Track progress every 10% if 100kb < filesize < 1mb. For larger + * files track progress for every percent transmitted. + * @param filesize the size of the file been transmitted + * @param totalLength the total transmission size + * @param percentTransmitted the current percent transmitted + * @return the percent that the file is of the total + */ + protected static int trackProgress(long filesize, long totalLength, + int percentTransmitted) { + + // CheckStyle:MagicNumber OFF + int percent = (int) Math.round(Math + .floor((totalLength / (double) filesize) * 100)); + + if (percent > percentTransmitted) { + if (filesize < 1048576) { + if (percent % 5 == 0) { + if (percent == 100) { + System.out.println(" 100%"); + } else { + System.out.print("*"); + } + } + } else { + if (percent == 50) { + System.out.println(" 50%"); + } else if (percent == 100) { + System.out.println(" 100%"); + } else { + System.out.print("."); + } + } + } + // CheckStyle:MagicNumber ON + + return percent; + } + + protected static void startRemoteCpProtocol(InputStream in, + OutputStream out, File localFile) throws IOException, SSHException { + File startFile = localFile; + while (true) { + // C0644 filesize filename - header for a regular file + // T time 0 time 0\n - present if perserve time. + // D directory - this is the header for a directory. + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + while (true) { + int read = in.read(); + if (read < 0) { + return; + } + if ((byte) read == LINE_FEED) { + break; + } + stream.write(read); + } + String serverResponse = stream.toString("UTF-8"); + if (serverResponse.charAt(0) == 'C') { + parseAndFetchFile(serverResponse, startFile, out, in); + } else if (serverResponse.charAt(0) == 'D') { + startFile = parseAndCreateDirectory(serverResponse, startFile); + sendAck(out); + } else if (serverResponse.charAt(0) == 'E') { + startFile = startFile.getParentFile(); + sendAck(out); + } else if (serverResponse.charAt(0) == '\01' + || serverResponse.charAt(0) == '\02') { + // this indicates an error. + throw new IOException(serverResponse.substring(1)); + } + } + } + + protected static File parseAndCreateDirectory(String serverResponse, + File localFile) { + int start = serverResponse.indexOf(" "); + // appears that the next token is not used and it's zero. + start = serverResponse.indexOf(" ", start + 1); + String directoryName = serverResponse.substring(start + 1); + if (localFile.isDirectory()) { + File dir = new File(localFile, directoryName); + dir.mkdir(); + log.debug("Creating: " + dir); + return dir; + } + return null; + } + + protected static void parseAndFetchFile(String serverResponse, + File localFile, OutputStream out, InputStream in) + throws IOException, SSHException { + int start = 0; + int end = serverResponse.indexOf(" ", start + 1); + start = end + 1; + end = serverResponse.indexOf(" ", start + 1); + long filesize = Long.parseLong(serverResponse.substring(start, end)); + String filename = serverResponse.substring(end + 1); + log.debug("Receiving: " + filename + " : " + filesize); + File transferFile = (localFile.isDirectory()) ? new File(localFile, + filename) : localFile; + fetchFile(transferFile, filesize, out, in); + waitForAck(in); + sendAck(out); + } + + protected static void fetchFile(File localFile, long filesize, + OutputStream out, InputStream in) throws IOException { + byte[] buf = new byte[BUFFER_SIZE]; + sendAck(out); + + // read a content of lfile + FileOutputStream fos = new FileOutputStream(localFile); + int length; + long totalLength = 0; + + // only track progress for files larger than 100kb in verbose mode + boolean trackProgress = filesize > HUNDRED_KILOBYTES; + // since filesize keeps on decreasing we have to store the + // initial filesize + long initFilesize = filesize; + int percentTransmitted = 0; + + try { + while (true) { + length = in.read(buf, 0, (BUFFER_SIZE < filesize) ? BUFFER_SIZE + : (int) filesize); + if (length < 0) { + throw new EOFException("Unexpected end of stream."); + } + fos.write(buf, 0, length); + filesize -= length; + totalLength += length; + if (filesize == 0) { + break; + } + + if (trackProgress) { + percentTransmitted = trackProgress(initFilesize, + totalLength, percentTransmitted); + } + } + } finally { + fos.flush(); + fos.close(); + } + } + + protected static void doSingleTransfer(Session session, File localFile, + String remoteFilePath) throws IOException, JSchException, + SSHException { + + String command = "scp -t \"" + remoteFilePath + "\""; + ChannelExec channel = (ChannelExec) session.openChannel("exec"); + channel.setCommand(command); + try { + + OutputStream out = channel.getOutputStream(); + InputStream in = channel.getInputStream(); + + channel.connect(); + + waitForAck(in); + sendFileToRemote(localFile, in, out); + } finally { + channel.disconnect(); + } + } + + protected static void sendFileToRemote(File localFile, InputStream in, + OutputStream out) throws IOException, SSHException { + // send "C0644 filesize filename", where filename should not include '/' + long filesize = localFile.length(); + String command = "C0644 " + filesize + " "; + command += localFile.getName(); + command += "\n"; + + out.write(command.getBytes()); + out.flush(); + + waitForAck(in); + + // send a content of lfile + FileInputStream fis = new FileInputStream(localFile); + byte[] buf = new byte[BUFFER_SIZE]; + long totalLength = 0; + + // only track progress for files larger than 100kb in verbose mode + boolean trackProgress = filesize > HUNDRED_KILOBYTES; + // since filesize keeps on decreasing we have to store the + // initial filesize + long initFilesize = filesize; + int percentTransmitted = 0; + + try { + while (true) { + int len = fis.read(buf, 0, buf.length); + if (len <= 0) { + break; + } + out.write(buf, 0, len); + totalLength += len; + + if (trackProgress) { + percentTransmitted = trackProgress(initFilesize, + totalLength, percentTransmitted); + } + } + out.flush(); + sendAck(out); + waitForAck(in); + } finally { + fis.close(); + } + } +} Property changes on: isis-fish/trunk/src/main/java/fr/ifremer/isisfish/simulator/launcher/ssh/SSHUtils.java ___________________________________________________________________ Name: svn:mergeinfo + Modified: isis-fish/trunk/src/main/resources/i18n/isis-fish-en_GB.properties =================================================================== --- isis-fish/trunk/src/main/resources/i18n/isis-fish-en_GB.properties 2009-01-23 09:22:36 UTC (rev 1753) +++ isis-fish/trunk/src/main/resources/i18n/isis-fish-en_GB.properties 2009-01-23 17:37:09 UTC (rev 1754) @@ -18,6 +18,8 @@ Can't\ simulate\ %s= Check\ state\ of\ local\ repository\:\ %s= Checkout\ pom.xml\ to\ %s= +Command\ '%s'\ fail\ to\ execute= +Control\ thread\ interrupted= Could\ not\ found\ formule\ type\ %s\ autorised\ type\ are\ %s= Error\ during\ vcs\ initialisation= Error\ while\ downloading\ simulation\ control= @@ -39,8 +41,10 @@ Month\ Gap\ BetweenRepro\ Recruitment= Normal\ stop\ thread,\ this\ is\ not\ an\ error= Not\ start\ simulation\ %s\ because\ user\ ask\ stop= +Process\ template\ error= Region\ %s\ allready\ exist\ in\ repository.\ Can't\ import= Region\ %s\ already\ inited= +Remote\ control\ file\ doen't\ exists\ %s= Rename\ data\ directory\ to\ %s= Show\ help= SimulationExecutor\ started\ with\ %s\ thread\ for\ %s= @@ -845,8 +849,8 @@ isisfish.simulation.name=Simulation name isisfish.simulation.remote.message.connection=Connecting to Caparmor isisfish.simulation.remote.message.downloadresults=Downloading results -isisfish.simulation.remote.message.launch=Launching simulation isisfish.simulation.remote.message.upload=Uploading simulation +isisfish.simulation.remote.message.waitingstart=Waiting for simulation start isisfish.simulation.title=Simulation launcher isisfish.simulator.launcher.inprocess=in current process isisfish.simulator.launcher.remote=on Caparmor server Modified: isis-fish/trunk/src/main/resources/i18n/isis-fish-fr_FR.properties =================================================================== --- isis-fish/trunk/src/main/resources/i18n/isis-fish-fr_FR.properties 2009-01-23 09:22:36 UTC (rev 1753) +++ isis-fish/trunk/src/main/resources/i18n/isis-fish-fr_FR.properties 2009-01-23 17:37:09 UTC (rev 1754) @@ -18,6 +18,8 @@ Can't\ simulate\ %s= Check\ state\ of\ local\ repository\:\ %s= Checkout\ pom.xml\ to\ %s= +Command\ '%s'\ fail\ to\ execute= +Control\ thread\ interrupted= Could\ not\ found\ formule\ type\ %s\ autorised\ type\ are\ %s= Error\ during\ vcs\ initialisation= Error\ while\ downloading\ simulation\ control= @@ -39,8 +41,10 @@ Month\ Gap\ BetweenRepro\ Recruitment= Normal\ stop\ thread,\ this\ is\ not\ an\ error= Not\ start\ simulation\ %s\ because\ user\ ask\ stop= +Process\ template\ error= Region\ %s\ allready\ exist\ in\ repository.\ Can't\ import= Region\ %s\ already\ inited=La r\u00E9gion %s a d\u00E9j\u00E0 \u00E9t\u00E9 initialis\u00E9e +Remote\ control\ file\ doen't\ exists\ %s= Rename\ data\ directory\ to\ %s= Show\ help= SimulationExecutor\ started\ with\ %s\ thread\ for\ %s= @@ -845,8 +849,8 @@ isisfish.simulation.name=Nom de la simulation isisfish.simulation.remote.message.connection=Connexion \u00E0 Caparmor isisfish.simulation.remote.message.downloadresults=T\u00E9l\u00E9chargement des r\u00E9sultats -isisfish.simulation.remote.message.launch=Lancement de la simulation isisfish.simulation.remote.message.upload=Upload de la simulation +isisfish.simulation.remote.message.waitingstart=Attente du d\u00E9marrage de la simulation isisfish.simulation.title=Lanceur de simulation isisfish.simulator.launcher.inprocess=dans le m\u00EAme processus isisfish.simulator.launcher.remote=sur le serveur Caparmor Added: isis-fish/trunk/src/main/resources/ssh/launch-isis-pxe.seq =================================================================== --- isis-fish/trunk/src/main/resources/ssh/launch-isis-pxe.seq (rev 0) +++ isis-fish/trunk/src/main/resources/ssh/launch-isis-pxe.seq 2009-01-23 17:37:09 UTC (rev 1754) @@ -0,0 +1,7 @@ +#!/bin/bash + +cd /home/chatellier/isis-fish-3.2.0.3 +#source /usr/share/modules/init/csh +#module load java/1.6.0 + +java -jar isis-fish-3.2.0.3.jar --option launch.ui false --simulateWithSimulation "${simulationid}" "${simulationzip}" Added: isis-fish/trunk/src/main/resources/ssh/launch-isis.seq =================================================================== --- isis-fish/trunk/src/main/resources/ssh/launch-isis.seq (rev 0) +++ isis-fish/trunk/src/main/resources/ssh/launch-isis.seq 2009-01-23 17:37:09 UTC (rev 1754) @@ -0,0 +1,9 @@ +#!/bin/csh + +#PBS -l mem=1000mb + +cd /export/home3/smahevas/isis-fish +source /usr/share/modules/init/csh +module load java/1.6.0 + +java -jar isis-fish-3.2.0.3/isis-fish.jar --option launch.ui false --simulateWithSimulation "${simulationid}" "${simulationzip}" Added: isis-fish/trunk/src/test/java/fr/ifremer/isisfish/simulator/launcher/SshSimulatorLauncherTest.java =================================================================== --- isis-fish/trunk/src/test/java/fr/ifremer/isisfish/simulator/launcher/SshSimulatorLauncherTest.java (rev 0) +++ isis-fish/trunk/src/test/java/fr/ifremer/isisfish/simulator/launcher/SshSimulatorLauncherTest.java 2009-01-23 17:37:09 UTC (rev 1754) @@ -0,0 +1,66 @@ +/* *##% + * Copyright (C) 2008 Code Lutin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +package fr.ifremer.isisfish.simulator.launcher; + +import java.io.IOException; + +import junit.framework.Assert; + +import org.junit.Test; + +import freemarker.template.TemplateException; + +/** + * Test portant sur la classe qui doit lancer des + * simulation au travers de SSH. + * + * @author chatellier + * @version $Revision: 1.0 $ + * + * Last update : $Date: 22 janv. 2009 $ + * By : $Author: chatellier $ + */ +public class SshSimulatorLauncherTest { + + /** + * Test l'instantiation du script qsub (caparmor). + * + * Le script contient une variable $simulation qui + * doit être remplacée. + * + * @throws IOException when freemarker throws it + * @throws TemplateException when freemarker throws it + */ + @Test + public void testFreemarkerTemplate() throws IOException, TemplateException { + + final String TEMPLATE = "ssh/launch-isis.seq"; + final String SIMULATIONID = "testid"; + final String SIMULATIONZIP = "test.zip"; + + SSHSimulatorLauncher launcher = new SSHSimulatorLauncher(); + String content = launcher.getSimulationScriptLaunchContent(TEMPLATE, SIMULATIONID, SIMULATIONZIP); + + Assert.assertTrue("String \"" + SIMULATIONID + "\" not found in template", + content.indexOf(SIMULATIONID) > 0); + + Assert.assertTrue("String \"" + SIMULATIONZIP + "\" not found in template", + content.indexOf(SIMULATIONZIP) > 0); + } +} \ No newline at end of file
participants (1)
-
chatellier@users.labs.libre-entreprise.org