Salome HOME
Synchronize adm files
[modules/kernel.git] / src / Launcher / Launcher_XML_Persistence.cxx
1 // Copyright (C) 2007-2014  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, or (at your option) any later version.
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 #include <libxml/parser.h>
24
25 #include "Launcher_XML_Persistence.hxx"
26 #include "Launcher_Job_Command.hxx"
27 #include "Launcher_Job_YACSFile.hxx"
28 #include "Launcher_Job_PythonSALOME.hxx"
29
30 using namespace std;
31
32 namespace Launcher
33 {
34
35 list<Job *>
36 XML_Persistence::loadJobs(const char* jobs_file)
37 {
38   // Step 1: check jobs_file read access
39   FILE* xml_file = fopen(jobs_file, "r");
40   if (xml_file == NULL)
41   {
42     std::string error = "Error opening jobs_file in SALOME_Launcher::loadJobs: " + std::string(jobs_file);
43     LAUNCHER_INFOS(error);
44     throw LauncherException(error);
45   }
46
47   // Step 2: read xml file
48   xmlDocPtr doc = xmlReadFile(jobs_file, NULL, 0);
49   if (doc == NULL)
50   {
51     std::string error = "Error in xmlReadFile in SALOME_Launcher::loadJobs, could not parse file: " + std::string(jobs_file);
52     LAUNCHER_INFOS(error);
53     fclose(xml_file);
54     throw LauncherException(error);
55   }
56
57   // Step 3: Find jobs
58   list<Job *> jobs_list;
59   xmlNodePtr root_node = xmlDocGetRootElement(doc);
60   if (xmlStrToString(root_node->name) == "jobs")
61   {
62     xmlNodePtr xmlCurrentNode = root_node->xmlChildrenNode;
63     while(xmlCurrentNode != NULL)
64     {
65       if (xmlStrToString(xmlCurrentNode->name) == "job")
66       {
67         LAUNCHER_INFOS("A job is found");
68         Job * new_job = createJobFromXmlNode(xmlCurrentNode);
69         jobs_list.push_back(new_job);
70       }
71       xmlCurrentNode = xmlCurrentNode->next;
72     }
73   }
74   else
75   {
76     xmlFreeDoc(doc);
77     fclose(xml_file);
78     std::string error = "Error in xml file, could not find root_node named jobs: " + std::string(jobs_file);
79     LAUNCHER_INFOS(error);
80     throw LauncherException(error);
81   }
82
83   // Clean
84   xmlFreeDoc(doc);
85   fclose(xml_file);
86
87   return jobs_list;
88 }
89
90 void
91 XML_Persistence::saveJobs(const char* jobs_file, const list<const Job *> & jobs_list)
92 {
93   // Step 1: check jobs_file write access
94   FILE* xml_file = fopen(jobs_file, "w");
95   if (xml_file == NULL)
96   {
97     std::string error = "Error opening jobs_file in SALOME_Launcher::saveJobs: " + std::string(jobs_file);
98     LAUNCHER_INFOS(error);
99     throw LauncherException(error);
100   }
101
102   // Step 2: First lines
103   xmlKeepBlanksDefault(0);
104   xmlDocPtr doc = xmlNewDoc(xmlCharStrdup("1.0"));
105   xmlNodePtr root_node = xmlNewNode(NULL, xmlCharStrdup("jobs"));
106   xmlDocSetRootElement(doc, root_node);
107   xmlNodePtr doc_comment = xmlNewDocComment(doc, xmlCharStrdup("SALOME Launcher save jobs file"));
108   xmlAddPrevSibling(root_node, doc_comment);
109
110   // Step 3: For each job write it on the xml document
111   // We could put a mutex but are not foing to do that currently
112   list<const Job *>::const_iterator it_job;
113   for (it_job = jobs_list.begin(); it_job != jobs_list.end(); it_job++)
114   {
115     addJobToXmlDocument(root_node, **it_job);
116   }
117
118   // Final step: write file
119   int isOk = xmlSaveFormatFile(jobs_file, doc, 1);
120   if (!isOk)
121   {
122     std::string error = "Error during xml file saving in SALOME_Launcher::saveJobs: " + std::string(jobs_file);
123     LAUNCHER_INFOS(error);
124     xmlFreeDoc(doc);
125     fclose(xml_file);
126     throw LauncherException(error);
127   }
128
129   // Clean
130   xmlFreeDoc(doc);
131   fclose(xml_file);
132   LAUNCHER_MESSAGE("SALOME_Launcher::saveJobs : WRITING DONE!");
133 }
134
135 void
136 XML_Persistence::addJobToXmlDocument(xmlNodePtr root_node, const Job & job)
137 {
138   // Begin job
139   xmlNodePtr job_node = addNode(root_node, "job", "");
140   addAttr(job_node, "type", job.getJobType());
141   addAttr(job_node, "name", job.getJobName());
142
143   // Add user part
144   xmlNodePtr node = addNode(job_node, "user_part", "");
145
146   addNode(node, "job_file", job.getJobFile());
147   if (!job.getEnvFile().empty())
148     addNode(node, "env_file", job.getEnvFile());
149   if (!job.getWorkDirectory().empty())
150     addNode(node, "work_directory", job.getWorkDirectory());
151   if (!job.getLocalDirectory().empty())
152     addNode(node, "local_directory", job.getLocalDirectory());
153   if (!job.getResultDirectory().empty())
154     addNode(node, "result_directory", job.getResultDirectory());
155
156   // Parameters for COORM
157   if (!job.getLauncherFile().empty())
158     addNode(node, "launcher_file", job.getLauncherFile());
159   if (!job.getLauncherArgs().empty())
160     addNode(node, "launcher_args", job.getLauncherArgs());
161
162   // Files
163   if ( ! (job.get_in_files().empty() && job.get_out_files().empty()) )
164   {
165     xmlNodePtr files_node = addNode(node, "files", "");
166     list<string> in_files = job.get_in_files();
167     list<string> out_files = job.get_out_files();
168     for(list<string>::iterator it = in_files.begin(); it != in_files.end(); it++)
169       addNode(files_node, "in_file", *it);
170     for(list<string>::iterator it = out_files.begin(); it != out_files.end(); it++)
171       addNode(files_node, "out_file", *it);
172   }
173
174   // Resource part
175   resourceParams resource_params = job.getResourceRequiredParams();
176   xmlNodePtr res_node = addNode(node, "resource_params", "");
177   addNode(res_node, "name", resource_params.name);
178   if (!resource_params.hostname.empty())
179     addNode(res_node, "hostname", resource_params.hostname);
180   if (!resource_params.OS.empty())
181     addNode(res_node, "OS", resource_params.OS);
182   if (resource_params.nb_proc > 0)
183     addNumericalNode(res_node, "nb_proc", resource_params.nb_proc);
184   if (resource_params.nb_node > 0)
185     addNumericalNode(res_node, "nb_node", resource_params.nb_node);
186   if (resource_params.nb_proc_per_node > 0)
187     addNumericalNode(res_node, "nb_proc_per_node", resource_params.nb_proc_per_node);
188   if (resource_params.cpu_clock > 0)
189     addNumericalNode(res_node, "cpu_clock", resource_params.cpu_clock);
190   if (resource_params.mem_mb > 0)
191     addNumericalNode(res_node, "mem_mb", resource_params.mem_mb);
192
193   if (!job.getMaximumDuration().empty())
194     addNode(node, "maximum_duration", job.getMaximumDuration());
195   if (!job.getQueue().empty())
196     addNode(node, "queue", job.getQueue());
197   if (job.getExclusive())
198     addNode(node, "exclusive", job.getExclusiveStr());
199   if (job.getMemPerCpu() > 0)
200     addNumericalNode(res_node, "mem_per_cpu", job.getMemPerCpu());
201
202   // Specific parameters part
203   map<string, string> specific_parameters = job.getSpecificParameters();
204   if (!specific_parameters.empty())
205   {
206     xmlNodePtr specific_parameters_node = addNode(node, "specific_parameters", "");
207     for(map<string, string>::iterator it = specific_parameters.begin();
208         it != specific_parameters.end();
209         it++)
210     {
211       xmlNodePtr specific_parameter_node = addNode(specific_parameters_node,
212                                                    "specific_parameter", "");
213       addNode(specific_parameter_node, "name", it->first);
214       addNode(specific_parameter_node, "value", it->second);
215     }
216   }
217
218   // Run part
219   xmlNodePtr run_node = addNode(job_node, "run_part", "");
220   addNode(run_node, "job_state", job.getState());
221   addNode(run_node, "job_reference", job.getReference());
222 }
223
224 Job *
225 XML_Persistence::createJobFromXmlNode(xmlNodePtr job_node)
226 {
227   Launcher::Job * new_job;
228
229   // Begin Job
230   string job_name = getAttrValue(job_node, "name");
231   if (job_name.empty())
232     throw LauncherException("Invalid job: name is not defined");
233   string job_type = getAttrValue(job_node, "type");
234   if (job_type.empty())
235     throw LauncherException(string("Invalid job \"") + job_name + "\": type is not defined");
236   if (job_type == "command")
237     new_job = new Launcher::Job_Command();
238   else if (job_type == "yacs_file")
239     new_job = new Launcher::Job_YACSFile();
240   else if (job_type == "python_salome")
241     new_job = new Launcher::Job_PythonSALOME();
242   else
243   {
244     string error = string("Invalid job \"") + job_name + "\": invalid type \"" + job_type + "\"";
245     throw LauncherException(error);
246   }
247   new_job->setJobName(job_name);
248
249   try
250   {
251     xmlNodePtr current_node = xmlFirstElementChild(job_node);
252     bool user_ok = false;
253     bool run_ok = false;
254     while (current_node != NULL)
255     {
256       string node_name = xmlStrToString(current_node->name);
257       if (node_name == "user_part")
258       {
259         parseUserNode(new_job, current_node);
260         user_ok = true;
261       }
262       else if (node_name == "run_part")
263       {
264         parseRunNode(new_job, current_node);
265         run_ok = true;
266       }
267       else
268         throw LauncherException(string("invalid node \"") + node_name + "\"");
269       current_node = xmlNextElementSibling(current_node);
270     }
271     if (!user_ok) throw LauncherException("missing user part");
272     if (!run_ok) throw LauncherException("missing run part");
273   }
274   catch (const LauncherException & exc)
275   {
276     delete new_job;
277     string error = string("Invalid job \"") + job_name + "\": " + exc.msg;
278     throw LauncherException(error);
279   }
280
281   return new_job;
282 }
283
284 void
285 XML_Persistence::parseUserNode(Job * new_job, xmlNodePtr user_node)
286 {
287   xmlNodePtr current_node = xmlFirstElementChild(user_node);
288   bool job_file_ok = false;
289   while (current_node != NULL)
290   {
291     string node_name = xmlStrToString(current_node->name);
292     if (node_name == "job_file")
293     {
294       new_job->setJobFile(getNodeContent(current_node));
295       job_file_ok = true;
296     }
297     else if (node_name == "env_file")
298       new_job->setEnvFile(getNodeContent(current_node));
299     else if (node_name == "work_directory")
300       new_job->setWorkDirectory(getNodeContent(current_node));
301     else if (node_name == "local_directory")
302       new_job->setLocalDirectory(getNodeContent(current_node));
303     else if (node_name == "result_directory")
304       new_job->setResultDirectory(getNodeContent(current_node));
305     else if (node_name == "launcher_file") // For COORM
306       new_job->setLauncherFile(getNodeContent(current_node));
307     else if (node_name == "launcher_args") // For COORM
308       new_job->setLauncherArgs(getNodeContent(current_node));
309     else if (node_name == "files")
310     {
311       // Get in and out files
312       xmlNodePtr file_node = xmlFirstElementChild(current_node);
313       while (file_node != NULL)
314       {
315         string file_node_name = xmlStrToString(file_node->name);
316         if (file_node_name == "in_file")
317           new_job->add_in_file(getNodeContent(file_node));
318         else if (file_node_name == "out_file")
319           new_job->add_out_file(getNodeContent(file_node));
320         else
321           throw LauncherException(string("invalid node \"") + file_node_name + "\"");
322         file_node = xmlNextElementSibling(file_node);
323       }
324     }
325     else if (node_name == "resource_params")
326       parseResourceNode(new_job, current_node);
327     else if (node_name == "maximum_duration")
328       new_job->setMaximumDuration(getNodeContent(current_node));
329     else if (node_name == "queue")
330       new_job->setQueue(getNodeContent(current_node));
331     else if (node_name == "exclusive")
332       new_job->setExclusiveStr(getNodeContent(current_node));
333     else if (node_name == "mem_per_cpu")
334       new_job->setMemPerCpu(getNumericalNodeContent<unsigned long>(current_node));
335     else if (node_name == "specific_parameters")
336     {
337       // Get specific parameters
338       xmlNodePtr parameter_node = xmlFirstElementChild(current_node);
339       while (parameter_node != NULL)
340       {
341         string parameter_node_name = xmlStrToString(parameter_node->name);
342         if (parameter_node_name == "specific_parameter")
343         {
344           xmlNodePtr inparam_node = xmlFirstElementChild(parameter_node);
345           string name;
346           string value;
347           while (inparam_node != NULL)
348           {
349             string inparam_node_name = xmlStrToString(inparam_node->name);
350             if (inparam_node_name == "name")
351               name = getNodeContent(inparam_node);
352             else if (inparam_node_name == "value")
353               value = getNodeContent(inparam_node);
354             else
355               throw LauncherException(string("invalid node \"") + inparam_node_name + "\"");
356             inparam_node = xmlNextElementSibling(inparam_node);
357           }
358           if (name.empty()) throw LauncherException("missing parameter name");
359           if (value.empty()) throw LauncherException("missing parameter value");
360           new_job->addSpecificParameter(name, value);
361         }
362         else
363           throw LauncherException(string("invalid node \"") + parameter_node_name + "\"");
364         parameter_node = xmlNextElementSibling(parameter_node);
365       }
366     }
367     else
368       throw LauncherException(string("invalid node \"") + node_name + "\"");
369     current_node = xmlNextElementSibling(current_node);
370   }
371   if (!job_file_ok) throw LauncherException("missing job file");
372 }
373
374 void
375 XML_Persistence::parseResourceNode(Job * new_job, xmlNodePtr res_node)
376 {
377   resourceParams p;
378   xmlNodePtr current_node = xmlFirstElementChild(res_node);
379   while (current_node != NULL)
380   {
381     string node_name = xmlStrToString(current_node->name);
382     if (node_name == "name")
383       p.name = getNodeContent(current_node);
384     else if (node_name == "hostname")
385       p.hostname = getNodeContent(current_node);
386     else if (node_name == "OS")
387       p.OS = getNodeContent(current_node);
388     else if (node_name == "nb_proc")
389       p.nb_proc = getNumericalNodeContent<long>(current_node);
390     else if (node_name == "nb_node")
391       p.nb_node = getNumericalNodeContent<long>(current_node);
392     else if (node_name == "nb_proc_per_node")
393       p.nb_proc_per_node = getNumericalNodeContent<long>(current_node);
394     else if (node_name == "cpu_clock")
395       p.cpu_clock = getNumericalNodeContent<long>(current_node);
396     else if (node_name == "mem_mb")
397       p.mem_mb = getNumericalNodeContent<long>(current_node);
398     else if (node_name == "mem_per_cpu")
399       new_job->setMemPerCpu(getNumericalNodeContent<long>(current_node));
400     else
401       throw LauncherException(string("invalid node \"") + node_name + "\"");
402     current_node = xmlNextElementSibling(current_node);
403   }
404   new_job->setResourceRequiredParams(p);
405 }
406
407 void
408 XML_Persistence::parseRunNode(Job * new_job, xmlNodePtr run_node)
409 {
410   xmlNodePtr current_node = xmlFirstElementChild(run_node);
411   while (current_node != NULL)
412   {
413     string node_name = xmlStrToString(current_node->name);
414     if (node_name == "job_state")
415       new_job->setState(getNodeContent(current_node));
416     else if (node_name == "resource_choosed_name")
417     {
418       // This parameter was present in older versions of Salome. Now we just silently ignore it.
419     }
420     else if (node_name == "job_reference")
421       new_job->setReference(getNodeContent(current_node));
422     else
423       throw LauncherException(string("invalid node \"") + node_name + "\"");
424     current_node = xmlNextElementSibling(current_node);
425   }
426 }
427
428 string
429 XML_Persistence::getAttrValue(xmlNodePtr node, const string & attrName)
430 {
431   string attrValue;
432   xmlChar * xmlAttrName = xmlCharStrdup(attrName.c_str());
433   xmlChar * xmlAttrValue = xmlGetProp(node, xmlAttrName);
434   if (xmlAttrValue != NULL) attrValue = (const char *)xmlAttrValue;
435   xmlFree(xmlAttrName);
436   xmlFree(xmlAttrValue);
437   return attrValue;
438 }
439
440 inline string
441 XML_Persistence::xmlStrToString(const xmlChar * xmlStr)
442 {
443   return string((const char *)xmlStr);
444 }
445
446 string
447 XML_Persistence::getNodeContent(xmlNodePtr node)
448 {
449   string nodeContent;
450   xmlChar * xmlStrContent = xmlNodeGetContent(node);
451   if (xmlStrContent != NULL) nodeContent = (const char *)xmlStrContent;
452   xmlFree(xmlStrContent);
453   return nodeContent;
454 }
455
456 template<typename T>
457 T
458 XML_Persistence::getNumericalNodeContent(xmlNodePtr node)
459 {
460   T result;
461   istringstream nodeContentStream(getNodeContent(node));
462   if (!(nodeContentStream >> result))
463     throw LauncherException(xmlStrToString(node->name) + " parameter is not correct");
464   return result;
465 }
466
467 xmlNodePtr
468 XML_Persistence::addNode(xmlNodePtr father, const string & name, const string & content)
469 {
470   xmlChar * xmlStrName = xmlCharStrdup(name.c_str());
471   xmlChar * xmlStrContent = NULL;
472   if (!content.empty())
473     xmlStrContent = xmlCharStrdup(content.c_str());
474   xmlNodePtr node = xmlNewChild(father, NULL, xmlStrName, xmlStrContent);
475   xmlFree(xmlStrName);
476   xmlFree(xmlStrContent);
477   return node;
478 }
479
480 template<typename T>
481 xmlNodePtr
482 XML_Persistence::addNumericalNode(xmlNodePtr father, const string & name, T content)
483 {
484   ostringstream nodeContentStream;
485   nodeContentStream << content;
486   return addNode(father, name, nodeContentStream.str());
487 }
488
489 void
490 XML_Persistence::addAttr(xmlNodePtr node, const string & name, const string & value)
491 {
492   xmlChar * xmlStrName = xmlCharStrdup(name.c_str());
493   xmlChar * xmlStrValue = xmlCharStrdup(value.c_str());
494   xmlNewProp(node, xmlStrName, xmlStrValue);
495   xmlFree(xmlStrName);
496   xmlFree(xmlStrValue);
497 }
498
499 }