Salome HOME
Merge branch 'omu/graphviz238'
[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   if (!job.getWCKey().empty())
202     addNode(node, "wckey", job.getWCKey());
203   if (!job.getExtraParams().empty())
204     addNode(node, "extra_params", job.getExtraParams());
205
206   // Specific parameters part
207   map<string, string> specific_parameters = job.getSpecificParameters();
208   if (!specific_parameters.empty())
209   {
210     xmlNodePtr specific_parameters_node = addNode(node, "specific_parameters", "");
211     for(map<string, string>::iterator it = specific_parameters.begin();
212         it != specific_parameters.end();
213         it++)
214     {
215       xmlNodePtr specific_parameter_node = addNode(specific_parameters_node,
216                                                    "specific_parameter", "");
217       addNode(specific_parameter_node, "name", it->first);
218       addNode(specific_parameter_node, "value", it->second);
219     }
220   }
221
222   // Run part
223   xmlNodePtr run_node = addNode(job_node, "run_part", "");
224   addNode(run_node, "job_state", job.getState());
225   addNode(run_node, "job_reference", job.getReference());
226 }
227
228 Job *
229 XML_Persistence::createJobFromXmlNode(xmlNodePtr job_node)
230 {
231   Launcher::Job * new_job;
232
233   // Begin Job
234   string job_name = getAttrValue(job_node, "name");
235   if (job_name.empty())
236     throw LauncherException("Invalid job: name is not defined");
237   string job_type = getAttrValue(job_node, "type");
238   if (job_type.empty())
239     throw LauncherException(string("Invalid job \"") + job_name + "\": type is not defined");
240   if (job_type == "command")
241     new_job = new Launcher::Job_Command();
242   else if (job_type == "yacs_file")
243     new_job = new Launcher::Job_YACSFile();
244   else if (job_type == "python_salome")
245     new_job = new Launcher::Job_PythonSALOME();
246   else
247   {
248     string error = string("Invalid job \"") + job_name + "\": invalid type \"" + job_type + "\"";
249     throw LauncherException(error);
250   }
251   new_job->setJobName(job_name);
252
253   try
254   {
255     xmlNodePtr current_node = xmlFirstElementChild(job_node);
256     bool user_ok = false;
257     bool run_ok = false;
258     while (current_node != NULL)
259     {
260       string node_name = xmlStrToString(current_node->name);
261       if (node_name == "user_part")
262       {
263         parseUserNode(new_job, current_node);
264         user_ok = true;
265       }
266       else if (node_name == "run_part")
267       {
268         parseRunNode(new_job, current_node);
269         run_ok = true;
270       }
271       else
272         throw LauncherException(string("invalid node \"") + node_name + "\"");
273       current_node = xmlNextElementSibling(current_node);
274     }
275     if (!user_ok) throw LauncherException("missing user part");
276     if (!run_ok) throw LauncherException("missing run part");
277   }
278   catch (const LauncherException & exc)
279   {
280     delete new_job;
281     string error = string("Invalid job \"") + job_name + "\": " + exc.msg;
282     throw LauncherException(error);
283   }
284
285   return new_job;
286 }
287
288 void
289 XML_Persistence::parseUserNode(Job * new_job, xmlNodePtr user_node)
290 {
291   xmlNodePtr current_node = xmlFirstElementChild(user_node);
292   bool job_file_ok = false;
293   while (current_node != NULL)
294   {
295     string node_name = xmlStrToString(current_node->name);
296     if (node_name == "job_file")
297     {
298       new_job->setJobFile(getNodeContent(current_node));
299       job_file_ok = true;
300     }
301     else if (node_name == "env_file")
302       new_job->setEnvFile(getNodeContent(current_node));
303     else if (node_name == "work_directory")
304       new_job->setWorkDirectory(getNodeContent(current_node));
305     else if (node_name == "local_directory")
306       new_job->setLocalDirectory(getNodeContent(current_node));
307     else if (node_name == "result_directory")
308       new_job->setResultDirectory(getNodeContent(current_node));
309     else if (node_name == "launcher_file") // For COORM
310       new_job->setLauncherFile(getNodeContent(current_node));
311     else if (node_name == "launcher_args") // For COORM
312       new_job->setLauncherArgs(getNodeContent(current_node));
313     else if (node_name == "files")
314     {
315       // Get in and out files
316       xmlNodePtr file_node = xmlFirstElementChild(current_node);
317       while (file_node != NULL)
318       {
319         string file_node_name = xmlStrToString(file_node->name);
320         if (file_node_name == "in_file")
321           new_job->add_in_file(getNodeContent(file_node));
322         else if (file_node_name == "out_file")
323           new_job->add_out_file(getNodeContent(file_node));
324         else
325           throw LauncherException(string("invalid node \"") + file_node_name + "\"");
326         file_node = xmlNextElementSibling(file_node);
327       }
328     }
329     else if (node_name == "resource_params")
330       parseResourceNode(new_job, current_node);
331     else if (node_name == "maximum_duration")
332       new_job->setMaximumDuration(getNodeContent(current_node));
333     else if (node_name == "queue")
334       new_job->setQueue(getNodeContent(current_node));
335     else if (node_name == "exclusive")
336       new_job->setExclusiveStr(getNodeContent(current_node));
337     else if (node_name == "mem_per_cpu")
338       new_job->setMemPerCpu(getNumericalNodeContent<unsigned long>(current_node));
339     else if (node_name == "wckey")
340       new_job->setWCKey(getNodeContent(current_node));
341     else if (node_name == "extra_params")
342       new_job->setExtraParams(getNodeContent(current_node));
343     else if (node_name == "specific_parameters")
344     {
345       // Get specific parameters
346       xmlNodePtr parameter_node = xmlFirstElementChild(current_node);
347       while (parameter_node != NULL)
348       {
349         string parameter_node_name = xmlStrToString(parameter_node->name);
350         if (parameter_node_name == "specific_parameter")
351         {
352           xmlNodePtr inparam_node = xmlFirstElementChild(parameter_node);
353           string name;
354           string value;
355           while (inparam_node != NULL)
356           {
357             string inparam_node_name = xmlStrToString(inparam_node->name);
358             if (inparam_node_name == "name")
359               name = getNodeContent(inparam_node);
360             else if (inparam_node_name == "value")
361               value = getNodeContent(inparam_node);
362             else
363               throw LauncherException(string("invalid node \"") + inparam_node_name + "\"");
364             inparam_node = xmlNextElementSibling(inparam_node);
365           }
366           if (name.empty()) throw LauncherException("missing parameter name");
367           if (value.empty()) throw LauncherException("missing parameter value");
368           new_job->addSpecificParameter(name, value);
369         }
370         else
371           throw LauncherException(string("invalid node \"") + parameter_node_name + "\"");
372         parameter_node = xmlNextElementSibling(parameter_node);
373       }
374     }
375     else
376       throw LauncherException(string("invalid node \"") + node_name + "\"");
377     current_node = xmlNextElementSibling(current_node);
378   }
379   if (!job_file_ok) throw LauncherException("missing job file");
380 }
381
382 void
383 XML_Persistence::parseResourceNode(Job * new_job, xmlNodePtr res_node)
384 {
385   resourceParams p;
386   xmlNodePtr current_node = xmlFirstElementChild(res_node);
387   while (current_node != NULL)
388   {
389     string node_name = xmlStrToString(current_node->name);
390     if (node_name == "name")
391       p.name = getNodeContent(current_node);
392     else if (node_name == "hostname")
393       p.hostname = getNodeContent(current_node);
394     else if (node_name == "OS")
395       p.OS = getNodeContent(current_node);
396     else if (node_name == "nb_proc")
397       p.nb_proc = getNumericalNodeContent<long>(current_node);
398     else if (node_name == "nb_node")
399       p.nb_node = getNumericalNodeContent<long>(current_node);
400     else if (node_name == "nb_proc_per_node")
401       p.nb_proc_per_node = getNumericalNodeContent<long>(current_node);
402     else if (node_name == "cpu_clock")
403       p.cpu_clock = getNumericalNodeContent<long>(current_node);
404     else if (node_name == "mem_mb")
405       p.mem_mb = getNumericalNodeContent<long>(current_node);
406     else if (node_name == "mem_per_cpu")
407       new_job->setMemPerCpu(getNumericalNodeContent<long>(current_node));
408     else
409       throw LauncherException(string("invalid node \"") + node_name + "\"");
410     current_node = xmlNextElementSibling(current_node);
411   }
412   new_job->setResourceRequiredParams(p);
413 }
414
415 void
416 XML_Persistence::parseRunNode(Job * new_job, xmlNodePtr run_node)
417 {
418   xmlNodePtr current_node = xmlFirstElementChild(run_node);
419   while (current_node != NULL)
420   {
421     string node_name = xmlStrToString(current_node->name);
422     if (node_name == "job_state")
423       new_job->setState(getNodeContent(current_node));
424     else if (node_name == "resource_choosed_name")
425     {
426       // This parameter was present in older versions of Salome. Now we just silently ignore it.
427     }
428     else if (node_name == "job_reference")
429       new_job->setReference(getNodeContent(current_node));
430     else
431       throw LauncherException(string("invalid node \"") + node_name + "\"");
432     current_node = xmlNextElementSibling(current_node);
433   }
434 }
435
436 string
437 XML_Persistence::getAttrValue(xmlNodePtr node, const string & attrName)
438 {
439   string attrValue;
440   xmlChar * xmlAttrName = xmlCharStrdup(attrName.c_str());
441   xmlChar * xmlAttrValue = xmlGetProp(node, xmlAttrName);
442   if (xmlAttrValue != NULL) attrValue = (const char *)xmlAttrValue;
443   xmlFree(xmlAttrName);
444   xmlFree(xmlAttrValue);
445   return attrValue;
446 }
447
448 inline string
449 XML_Persistence::xmlStrToString(const xmlChar * xmlStr)
450 {
451   return string((const char *)xmlStr);
452 }
453
454 string
455 XML_Persistence::getNodeContent(xmlNodePtr node)
456 {
457   string nodeContent;
458   xmlChar * xmlStrContent = xmlNodeGetContent(node);
459   if (xmlStrContent != NULL) nodeContent = (const char *)xmlStrContent;
460   xmlFree(xmlStrContent);
461   return nodeContent;
462 }
463
464 template<typename T>
465 T
466 XML_Persistence::getNumericalNodeContent(xmlNodePtr node)
467 {
468   T result;
469   istringstream nodeContentStream(getNodeContent(node));
470   if (!(nodeContentStream >> result))
471     throw LauncherException(xmlStrToString(node->name) + " parameter is not correct");
472   return result;
473 }
474
475 xmlNodePtr
476 XML_Persistence::addNode(xmlNodePtr father, const string & name, const string & content)
477 {
478   xmlChar * xmlStrName = xmlCharStrdup(name.c_str());
479   xmlChar * xmlStrContent = NULL;
480   if (!content.empty())
481     xmlStrContent = xmlCharStrdup(content.c_str());
482   xmlNodePtr node = xmlNewChild(father, NULL, xmlStrName, xmlStrContent);
483   xmlFree(xmlStrName);
484   xmlFree(xmlStrContent);
485   return node;
486 }
487
488 template<typename T>
489 xmlNodePtr
490 XML_Persistence::addNumericalNode(xmlNodePtr father, const string & name, T content)
491 {
492   ostringstream nodeContentStream;
493   nodeContentStream << content;
494   return addNode(father, name, nodeContentStream.str());
495 }
496
497 void
498 XML_Persistence::addAttr(xmlNodePtr node, const string & name, const string & value)
499 {
500   xmlChar * xmlStrName = xmlCharStrdup(name.c_str());
501   xmlChar * xmlStrValue = xmlCharStrdup(value.c_str());
502   xmlNewProp(node, xmlStrName, xmlStrValue);
503   xmlFree(xmlStrName);
504   xmlFree(xmlStrValue);
505 }
506
507 }