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