Salome HOME
1a6a488a952ffb8d5bfc5089c8b2e384cef42c7b
[tools/libbatch.git] / src / Core / Batch_BatchManager_eClient.cxx
1 //  Copyright (C) 2007-2010  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 <stdlib.h>
33 #include <string.h>
34
35 #include <ctime>
36 #include <iostream>
37 #include <fstream>
38 #include <sstream>
39
40 #include <stdlib.h>
41
42 #ifdef WIN32
43 #include <direct.h>
44 #include <io.h>
45 #endif
46
47 #include <Batch_config.h>
48
49 #include "Batch_BatchManager_eClient.hxx"
50 #include "Batch_RunTimeException.hxx"
51
52 #ifdef MSVC
53 #define EXISTS(path) _access_s(path, 0) == 0
54 #else
55 #define EXISTS(path) access(path, F_OK) == 0
56 #endif
57
58 using namespace std;
59
60
61 namespace Batch {
62
63   BatchManager_eClient::BatchManager_eClient(const Batch::FactBatchManager * parent, const char* host,
64                                              CommunicationProtocolType protocolType, const char* mpiImpl)
65     : BatchManager(parent, host), _protocol(CommunicationProtocol::getInstance(protocolType)), _username("")
66   {
67     // instanciation of mpi implementation needed to launch executable in batch script
68     _mpiImpl = FactoryMpiImpl(mpiImpl);
69   }
70
71   // Destructeur
72   BatchManager_eClient::~BatchManager_eClient()
73   {
74     if (_mpiImpl)
75       delete _mpiImpl;
76   }
77
78   void BatchManager_eClient::exportInputFiles(const Job& job)
79   {
80     int status;
81     Parametre params = job.getParametre();
82     Versatile V = params[INFILE];
83     Versatile::iterator Vit;
84
85     status = _protocol.makeDirectory(string(params[TMPDIR]) + "/logs", _hostname, _username);
86     if(status) {
87       std::ostringstream oss;
88       oss << status;
89       std::string ex_mess("Error of connection on remote host ! status = ");
90       ex_mess += oss.str();
91       throw EmulationException(ex_mess.c_str());
92     }
93
94     // Second step : copy fileToExecute into
95     // batch tmp files directory
96     string executeFile = params[EXECUTABLE];
97     if (executeFile.size() != 0) {
98       status = _protocol.copyFile(executeFile, "", "",
99                                   params[TMPDIR], _hostname, _username);
100       if(status) {
101         std::ostringstream oss;
102         oss << status;
103         std::string ex_mess("Error of connection on remote host ! status = ");
104         ex_mess += oss.str();
105         throw EmulationException(ex_mess.c_str());
106       }
107
108 #ifdef WIN32
109       // On Windows, we make the remote file executable afterward because
110       // pscp does not preserve access permissions on files
111       string subCommand = string("chmod u+x ") + string(params[TMPDIR]) + "/" +
112                           string(params[EXECUTABLE]);
113       string command = _protocol.getExecCommand(subCommand, _hostname, _username);
114       cerr << command.c_str() << endl;
115       status = system(command.c_str());
116       if(status) {
117         std::ostringstream oss;
118         oss << status;
119         std::string ex_mess("Error of connection on remote host ! status = ");
120         ex_mess += oss.str();
121         throw EmulationException(ex_mess.c_str());
122       }
123 #endif
124     }
125
126     // Third step : copy filesToExportList into
127     // batch tmp files directory
128     for(Vit=V.begin(); Vit!=V.end(); Vit++) {
129       CoupleType cpt  = *static_cast< CoupleType * >(*Vit);
130       Couple inputFile = cpt;
131       status = _protocol.copyFile(inputFile.getLocal(), "", "",
132                                   inputFile.getRemote(), _hostname, _username);
133       if(status) {
134         std::ostringstream oss;
135         oss << status;
136         std::string ex_mess("Error of connection on remote host ! status = ");
137         ex_mess += oss.str();
138         throw EmulationException(ex_mess.c_str());
139       }
140     }
141
142   }
143
144   void BatchManager_eClient::importOutputFiles( const Job & job, const string directory ) throw(EmulationException)
145   {
146     Parametre params = job.getParametre();
147     Versatile V = params[OUTFILE];
148     Versatile::iterator Vit;
149     _username = string(params[USER]);
150
151     // Create local result directory
152     int status = CommunicationProtocol::getInstance(SH).makeDirectory(directory, "", "");
153     if (status) {
154       string mess("Directory creation failed. Status is :");
155       ostringstream status_str;
156       status_str << status;
157       mess += status_str.str();
158       cerr << mess << endl;
159     }
160
161     for(Vit=V.begin(); Vit!=V.end(); Vit++) {
162       CoupleType cpt  = *static_cast< CoupleType * >(*Vit);
163       Couple outputFile = cpt;
164       status = _protocol.copyFile(outputFile.getRemote(), _hostname, _username,
165                                   directory, "", "");
166       if (status) {
167         // Try to get what we can (logs files)
168         // throw BatchException("Error of connection on remote host");
169         std::string mess("Copy command failed ! status is :");
170         ostringstream status_str;
171         status_str << status;
172         mess += status_str.str();
173         cerr << mess << endl;
174       }
175     }
176
177     // Copy logs
178     status = _protocol.copyFile(string(params[TMPDIR]) + string("/logs"), _hostname, _username,
179                                 directory, "", "");
180     if (status) {
181       std::string mess("Copy logs directory failed ! status is :");
182       ostringstream status_str;
183       status_str << status;
184       mess += status_str.str();
185       cerr << mess << endl;
186     }
187
188   }
189
190   MpiImpl *BatchManager_eClient::FactoryMpiImpl(string mpiImpl) throw(EmulationException)
191   {
192     if(mpiImpl == "lam")
193       return new MpiImpl_LAM();
194     else if(mpiImpl == "mpich1")
195       return new MpiImpl_MPICH1();
196     else if(mpiImpl == "mpich2")
197       return new MpiImpl_MPICH2();
198     else if(mpiImpl == "openmpi")
199       return new MpiImpl_OPENMPI();
200     else if(mpiImpl == "slurm")
201       return new MpiImpl_SLURM();
202     else if(mpiImpl == "prun")
203       return new MpiImpl_PRUN();
204     else if(mpiImpl == "nompi")
205       return NULL;
206     else{
207       ostringstream oss;
208       oss << mpiImpl << " : not yet implemented";
209       throw EmulationException(oss.str().c_str());
210     }
211   }
212
213   /**
214    * This method generates a temporary file name with the pattern "<tmpdir>/<prefix>-XXXXXX" where
215    * <tmpdir> is the directory for temporary files (see BatchManager_eClient::getTmpDir()) and the
216    * X's are replaced by random characters. Note that this method is less secure than
217    * BatchManager_eClient::createAndOpenTemporaryFile, so use the latter whenever possible.
218    * \param prefix the prefix to use for the temporary file.
219    * \return a name usable for a temporary file.
220    */
221   string BatchManager_eClient::generateTemporaryFileName(const string & prefix)
222   {
223     string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
224     char randstr[7];
225
226     do {
227       sprintf(randstr, "%06d", rand() % 1000000);
228       fileName.replace(fileName.size()-6, 6, randstr);
229     } while (EXISTS(fileName.c_str()));
230
231     return fileName;
232   }
233
234   /**
235    * This method creates a temporary file and opens an output stream to write into this file.
236    * The file is created with the pattern "<tmpdir>/<prefix>-XXXXXX" where <tmpdir> is the directory
237    * for temporary files (see BatchManager_eClient::getTmpDir()) and the X's are replaced by random
238    * characters. The caller is responsible for closing and deleting the file when it is no more used.
239    * \param prefix the prefix to use for the temporary file.
240    * \param outputStream an output stream that will be opened for writing in the temporary file. If
241    * the stream is already open, it will be closed first.
242    * \return the name of the created file.
243    */
244   string BatchManager_eClient::createAndOpenTemporaryFile(const string & prefix, ofstream & outputStream)
245   {
246     if (outputStream.is_open())
247       outputStream.close();
248
249 #ifdef WIN32
250
251     string fileName = generateTemporaryFileName(prefix);
252     outputStream.open(fileName.c_str());
253
254 #else
255
256     string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
257     char * buf = new char[fileName.size()+1];
258     fileName.copy(buf, fileName.size());
259     buf[fileName.size()] = '\0';
260
261     int fd = mkstemp(buf);
262     if (fd == -1) {
263       delete[] buf;
264       throw RunTimeException(string("Can't create temporary file ") + fileName);
265     }
266     fileName = buf;
267     delete[] buf;
268
269     outputStream.open(fileName.c_str());
270     close(fd);  // Close the file descriptor so that the file is not opened twice
271
272 #endif
273
274     if (outputStream.fail())
275       throw RunTimeException(string("Can't open temporary file ") + fileName);
276
277     return fileName;
278   }
279
280   /**
281    * This method finds the name of the directory to use for temporary files in libBatch. This name
282    * is <tempdir>/libBatch-<username>-XXXXXX. <tempdir> is found by looking for environment
283    * variables TEMP, TMP, TEMPDIR, TMPDIR, and defaults to "/tmp" if none of them is defined.
284    * <username> is found by looking for environment variables USER and USERNAME, and defaults to
285    * "unknown". XXXXXX represents random characters. The directory name is generated only once for
286    * each BatchManager_eClient instance, and the directory is created at this moment. Subsequent
287    * calls will always return the same path and the existence of the directory will not be
288    * rechecked.
289    * \return the name of the directory to use for temporary files.
290    */
291   const std::string & BatchManager_eClient::getTmpDir()
292   {
293     if (tmpDirName.empty()) {
294       const char * baseDir = getenv("TEMP");
295       if (baseDir == NULL) baseDir = getenv("TMP");
296       if (baseDir == NULL) baseDir = getenv("TEMPDIR");
297       if (baseDir == NULL) baseDir = getenv("TMPDIR");
298       if (baseDir == NULL) baseDir = "/tmp";
299
300       const char * userName = getenv("USER");
301       if (userName == NULL) userName = getenv("USERNAME");
302       if (userName == NULL) userName = "unknown";
303
304       string baseName = string(baseDir) + "/libBatch-" + userName + "-XXXXXX";
305       srand(time(NULL));
306
307 #ifdef WIN32
308
309       char randstr[7];
310       do {
311         sprintf(randstr, "%06d", rand() % 1000000);
312         baseName.replace(baseName.size()-6, 6, randstr);
313       } while (EXISTS(baseName.c_str()));
314       if (_mkdir(baseName.c_str()) != 0)
315         throw RunTimeException(string("Can't create temporary directory ") + baseName);
316       tmpDirName = baseName;
317
318 #else
319
320       char * buf = new char[baseName.size() + 1];
321       baseName.copy(buf, baseName.size());
322       buf[baseName.size()] = '\0';
323       if (mkdtemp(buf) == NULL) {
324         delete[] buf;
325         throw RunTimeException(string("Can't create temporary directory ") + baseName);
326       }
327       tmpDirName = buf;
328       delete[] buf;
329
330 #endif
331
332     }
333
334     return tmpDirName;
335   }
336
337 }