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