Salome HOME
Refactored submission based on SH, RSH and SSH by grouping related commands in Commun...
[tools/libbatch.git] / src / Core / Batch_BatchManager_eClient.cxx
1 //  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 //  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 //  This library is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU Lesser General Public
8 //  License as published by the Free Software Foundation; either
9 //  version 2.1 of the License.
10 //
11 //  This library is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //  Lesser General Public License for more details.
15 //
16 //  You should have received a copy of the GNU Lesser General Public
17 //  License along with this library; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 /*
23 * BatchManager_eLSF.cxx : emulation of LSF client
24 *
25 * Auteur : Bernard SECHER - CEA DEN
26 * Mail   : mailto:bernard.secher@cea.fr
27 * Date   : Thu Apr 24 10:17:22 2008
28 * Projet : PAL Salome
29 *
30 */
31
32 #include <ctime>
33 #include <iostream>
34 #include <fstream>
35 #include <sstream>
36
37 #ifdef WIN32
38 #include <direct.h>
39 #include <io.h>
40 #endif
41
42 #include <Batch_config.h>
43
44 #include "Batch_BatchManager_eClient.hxx"
45 #include "Batch_RunTimeException.hxx"
46
47 #ifdef MSVC
48 #define EXISTS(path) _access_s(path, 0) == 0
49 #else
50 #define EXISTS(path) access(path, F_OK) == 0
51 #endif
52
53 using namespace std;
54
55
56 namespace Batch {
57
58   BatchManager_eClient::BatchManager_eClient(const Batch::FactBatchManager * parent, const char* host,
59                                              CommunicationProtocolType protocolType, const char* mpiImpl)
60     : BatchManager(parent, host), _protocol(CommunicationProtocol::getInstance(protocolType)), _username("")
61   {
62     // instanciation of mpi implementation needed to launch executable in batch script
63     _mpiImpl = FactoryMpiImpl(mpiImpl);
64   }
65
66   // Destructeur
67   BatchManager_eClient::~BatchManager_eClient()
68   {
69     delete _mpiImpl;
70   }
71
72   void BatchManager_eClient::exportInputFiles(const Job& job)
73   {
74     int status;
75     Parametre params = job.getParametre();
76     Versatile V = params[INFILE];
77     Versatile::iterator Vit;
78     _username = string(params[USER]);
79
80     string subCommand = string("mkdir -p ") + string(params[TMPDIR]);
81     string command = _protocol.getExecCommand(subCommand, _hostname, _username);
82     cerr << command.c_str() << endl;
83     status = system(command.c_str());
84     if(status) {
85       std::ostringstream oss;
86       oss << status;
87       std::string ex_mess("Error of connection on remote host ! status = ");
88       ex_mess += oss.str();
89       throw EmulationException(ex_mess.c_str());
90     }
91
92     // Second step : copy fileToExecute into
93     // batch tmp files directory
94     string executeFile = params[EXECUTABLE];
95     if (executeFile.size() != 0) {
96       status = _protocol.copyFile(executeFile, "", "",
97                                   params[TMPDIR], _hostname, _username);
98       if(status) {
99         std::ostringstream oss;
100         oss << status;
101         std::string ex_mess("Error of connection on remote host ! status = ");
102         ex_mess += oss.str();
103         throw EmulationException(ex_mess.c_str());
104       }
105
106 #ifdef WIN32
107       // On Windows, we make the remote file executable afterward because
108       // pscp does not preserve access permissions on files
109       subCommand = string("chmod u+x ") + string(params[TMPDIR]) + "/" +
110                    string(params[EXECUTABLE]);
111       command = _protocol.getExecCommand(subCommand, _hostname, _username);
112       cerr << command.c_str() << endl;
113       status = system(command.c_str());
114       if(status) {
115         std::ostringstream oss;
116         oss << status;
117         std::string ex_mess("Error of connection on remote host ! status = ");
118         ex_mess += oss.str();
119         throw EmulationException(ex_mess.c_str());
120       }
121 #endif
122     }
123
124     // Third step : copy filesToExportList into
125     // batch tmp files directory
126     for(Vit=V.begin(); Vit!=V.end(); Vit++) {
127       CoupleType cpt  = *static_cast< CoupleType * >(*Vit);
128       Couple inputFile = cpt;
129       status = _protocol.copyFile(inputFile.getLocal(), "", "",
130                                   inputFile.getRemote(), _hostname, _username);
131       if(status) {
132         std::ostringstream oss;
133         oss << status;
134         std::string ex_mess("Error of connection on remote host ! status = ");
135         ex_mess += oss.str();
136         throw EmulationException(ex_mess.c_str());
137       }
138     }
139
140   }
141
142   void BatchManager_eClient::importOutputFiles( const Job & job, const string directory ) throw(EmulationException)
143   {
144     Parametre params = job.getParametre();
145     Versatile V = params[OUTFILE];
146     Versatile::iterator Vit;
147
148     for(Vit=V.begin(); Vit!=V.end(); Vit++) {
149       CoupleType cpt  = *static_cast< CoupleType * >(*Vit);
150       Couple outputFile = cpt;
151       int status = _protocol.copyFile(outputFile.getRemote(), _hostname, _username,
152                                       directory, "", "");
153       if (status) {
154         // Try to get what we can (logs files)
155         // throw BatchException("Error of connection on remote host");
156         std::string mess("Copy command failed ! status is :");
157         ostringstream status_str;
158         status_str << status;
159         mess += status_str.str();
160         cerr << mess << endl;
161       }
162     }
163
164   }
165
166   MpiImpl *BatchManager_eClient::FactoryMpiImpl(string mpiImpl) throw(EmulationException)
167   {
168     if(mpiImpl == "lam")
169       return new MpiImpl_LAM();
170     else if(mpiImpl == "mpich1")
171       return new MpiImpl_MPICH1();
172     else if(mpiImpl == "mpich2")
173       return new MpiImpl_MPICH2();
174     else if(mpiImpl == "openmpi")
175       return new MpiImpl_OPENMPI();
176     else if(mpiImpl == "slurm")
177       return new MpiImpl_SLURM();
178     else if(mpiImpl == "prun")
179       return new MpiImpl_PRUN();
180     else if(mpiImpl == "nompi")
181       throw EmulationException("you must specify an mpi implementation for batch manager");
182     else{
183       ostringstream oss;
184       oss << mpiImpl << " : not yet implemented";
185       throw EmulationException(oss.str().c_str());
186     }
187   }
188
189   /**
190    * This method generates a temporary file name with the pattern "<tmpdir>/<prefix>-XXXXXX" where
191    * <tmpdir> is the directory for temporary files (see BatchManager_eClient::getTmpDir()) and the
192    * X's are replaced by random characters. Note that this method is less secure than
193    * BatchManager_eClient::createAndOpenTemporaryFile, so use the latter whenever possible.
194    * \param prefix the prefix to use for the temporary file.
195    * \return a name usable for a temporary file.
196    */
197   string BatchManager_eClient::generateTemporaryFileName(const string & prefix)
198   {
199     string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
200     char randstr[7];
201
202     do {
203       sprintf(randstr, "%06d", rand() % 1000000);
204       fileName.replace(fileName.size()-6, 6, randstr);
205     } while (EXISTS(fileName.c_str()));
206
207     return fileName;
208   }
209
210   /**
211    * This method creates a temporary file and opens an output stream to write into this file.
212    * The file is created with the pattern "<tmpdir>/<prefix>-XXXXXX" where <tmpdir> is the directory
213    * for temporary files (see BatchManager_eClient::getTmpDir()) and the X's are replaced by random
214    * characters. The caller is responsible for closing and deleting the file when it is no more used.
215    * \param prefix the prefix to use for the temporary file.
216    * \param outputStream an output stream that will be opened for writing in the temporary file. If
217    * the stream is already open, it will be closed first.
218    * \return the name of the created file.
219    */
220   string BatchManager_eClient::createAndOpenTemporaryFile(const string & prefix, ofstream & outputStream)
221   {
222     if (outputStream.is_open())
223       outputStream.close();
224
225 #ifdef WIN32
226
227     string fileName = generateTemporaryFileName(prefix);
228     outputStream.open(fileName.c_str());
229
230 #else
231
232     string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
233     char * buf = new char[fileName.size()+1];
234     fileName.copy(buf, fileName.size());
235     buf[fileName.size()] = '\0';
236
237     int fd = mkstemp(buf);
238     if (fd == -1) {
239       delete[] buf;
240       throw RunTimeException(string("Can't create temporary file ") + fileName);
241     }
242     fileName = buf;
243     delete[] buf;
244
245     outputStream.open(fileName.c_str());
246     close(fd);  // Close the file descriptor so that the file is not opened twice
247
248 #endif
249
250     if (outputStream.fail())
251       throw RunTimeException(string("Can't open temporary file ") + fileName);
252
253     return fileName;
254   }
255
256   /**
257    * This method finds the name of the directory to use for temporary files in libBatch. This name
258    * is <tempdir>/libBatch-<username>-XXXXXX. <tempdir> is found by looking for environment
259    * variables TEMP, TMP, TEMPDIR, TMPDIR, and defaults to "/tmp" if none of them is defined.
260    * <username> is found by looking for environment variables USER and USERNAME, and defaults to
261    * "unknown". XXXXXX represents random characters. The directory name is generated only once for
262    * each BatchManager_eClient instance, and the directory is created at this moment. Subsequent
263    * calls will always return the same path and the existence of the directory will not be
264    * rechecked.
265    * \return the name of the directory to use for temporary files.
266    */
267   const std::string & BatchManager_eClient::getTmpDir()
268   {
269     if (tmpDirName.empty()) {
270       char * baseDir = getenv("TEMP");
271       if (baseDir == NULL) baseDir = getenv("TMP");
272       if (baseDir == NULL) baseDir = getenv("TEMPDIR");
273       if (baseDir == NULL) baseDir = getenv("TMPDIR");
274       if (baseDir == NULL) baseDir = "/tmp";
275
276       char * userName = getenv("USER");
277       if (userName == NULL) userName = getenv("USERNAME");
278       if (userName == NULL) userName = "unknown";
279
280       string baseName = string(baseDir) + "/libBatch-" + userName + "-XXXXXX";
281       srand(time(NULL));
282
283 #ifdef WIN32
284
285       char randstr[7];
286       do {
287         sprintf(randstr, "%06d", rand() % 1000000);
288         baseName.replace(baseName.size()-6, 6, randstr);
289       } while (EXISTS(baseName.c_str()));
290       if (_mkdir(baseName.c_str()) != 0)
291         throw RunTimeException(string("Can't create temporary directory ") + baseName);
292       tmpDirName = baseName;
293
294 #else
295
296       char * buf = new char[baseName.size() + 1];
297       baseName.copy(buf, baseName.size());
298       buf[baseName.size()] = '\0';
299       if (mkdtemp(buf) == NULL) {
300         delete[] buf;
301         throw RunTimeException(string("Can't create temporary directory ") + baseName);
302       }
303       tmpDirName = buf;
304       delete[] buf;
305
306 #endif
307
308     }
309
310     return tmpDirName;
311   }
312
313 }