Salome HOME
30b32efe9b6048199819e6b94e571cc6fb5d8497
[modules/kernel.git] / src / ResourcesManager / ResourcesManager.cxx
1 // Copyright (C) 2007-2013  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 #include "ResourcesManager.hxx" 
24 #include <Basics_Utils.hxx>
25 #include <fstream>
26 #include <iostream>
27 #include <sstream>
28 #include <string.h>
29 #include <map>
30 #include <list>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #ifdef WNT
34 #else
35 #include <unistd.h>
36 #endif
37 #include <libxml/parser.h>
38
39 #include <algorithm>
40
41 #include "Utils_SALOME_Exception.hxx"
42
43 #define MAX_SIZE_FOR_HOSTNAME 256;
44
45 using namespace std;
46
47 const string ResourcesManager_cpp::DEFAULT_RESOURCE_NAME = "localhost";
48
49 static LoadRateManagerFirst first;
50 static LoadRateManagerCycl cycl;
51 static LoadRateManagerAltCycl altcycl;
52 //=============================================================================
53 /*!
54  * just for test
55  */ 
56 //=============================================================================
57
58 ResourcesManager_cpp::
59 ResourcesManager_cpp(const char *xmlFilePath)
60 {
61   _path_resources.push_back(xmlFilePath);
62 #if defined(_DEBUG_) || defined(_DEBUG)
63   std::cerr << "ResourcesManager_cpp constructor" << std::endl;
64 #endif
65   _resourceManagerMap["first"]=&first;
66   _resourceManagerMap["cycl"]=&cycl;
67   _resourceManagerMap["altcycl"]=&altcycl;
68   _resourceManagerMap["best"]=&altcycl;
69   _resourceManagerMap[""]=&altcycl;
70
71   AddDefaultResourceInCatalog();
72 }
73
74 //=============================================================================
75 /*!
76  *  Standard constructor, parse resource file.
77  *  - if ${APPLI} exists in environment,
78  *    look for ${HOME}/${APPLI}/CatalogResources.xml
79  *  - else look for default:
80  *    ${KERNEL_ROOT_DIR}/share/salome/resources/kernel/CatalogResources.xml
81  *  - parse XML resource file.
82  */ 
83 //=============================================================================
84
85 ResourcesManager_cpp::ResourcesManager_cpp() throw(ResourcesException)
86 {
87   RES_MESSAGE("ResourcesManager_cpp constructor");
88
89   _resourceManagerMap["first"]=&first;
90   _resourceManagerMap["cycl"]=&cycl;
91   _resourceManagerMap["altcycl"]=&altcycl;
92   _resourceManagerMap["best"]=&altcycl;
93   _resourceManagerMap[""]=&altcycl;
94
95   AddDefaultResourceInCatalog();
96
97   bool default_catalog_resource = true;
98   if (getenv("USER_CATALOG_RESOURCES_FILE") != 0)
99   {
100     default_catalog_resource = false;
101     std::string user_file("");
102     user_file = getenv("USER_CATALOG_RESOURCES_FILE");
103     std::ifstream ifile(user_file.c_str(), std::ifstream::in );
104     if (ifile) {
105       // The file exists, and is open for input
106       _path_resources.push_back(user_file);
107     }
108     else {
109       default_catalog_resource = false;
110       RES_INFOS("Warning: USER_CATALOG_RESOURCES_FILE is set and file cannot be found.")
111       RES_INFOS("Warning: That's why we try to create a new one.")
112       std::ofstream user_catalog_file;
113       user_catalog_file.open(user_file.c_str());
114       if (user_catalog_file.fail())
115       {
116         RES_INFOS("Error: cannot write in the user catalog resouces files");
117         RES_INFOS("Error: using default CatalogResources.xml file");
118         default_catalog_resource = true;
119       }
120       else
121       {
122         user_catalog_file << "<!-- File created by SALOME -->" << std::endl;
123         user_catalog_file << "<!DOCTYPE ResourcesCatalog>" << std::endl;
124         user_catalog_file << "<resources>" << std::endl;
125         user_catalog_file << "   <machine name=\"localhost\" hostname=\"localhost\" />" << std::endl;
126         user_catalog_file << "</resources>" << std::endl;
127         user_catalog_file.close();
128       }
129     }
130   }
131   if (default_catalog_resource)
132   {
133     std::string default_file("");
134     if (getenv("APPLI") != 0)
135     {
136       default_file += getenv("HOME");
137       default_file += "/";
138       default_file += getenv("APPLI");
139       default_file += "/CatalogResources.xml";
140       _path_resources.push_back(default_file);
141     }
142     else
143     {
144       if(!getenv("KERNEL_ROOT_DIR"))
145         throw ResourcesException("you must define KERNEL_ROOT_DIR environment variable!! -> cannot load a CatalogResources.xml");
146       default_file = getenv("KERNEL_ROOT_DIR");
147       default_file += "/share/salome/resources/kernel/CatalogResources.xml";
148       _path_resources.push_back(default_file);
149     }
150   }
151
152   _lasttime=0;
153
154   ParseXmlFiles();
155   RES_MESSAGE("ResourcesManager_cpp constructor end");
156 }
157
158 //=============================================================================
159 /*!
160  *  Standard Destructor
161  */ 
162 //=============================================================================
163
164 ResourcesManager_cpp::~ResourcesManager_cpp()
165 {
166   RES_MESSAGE("ResourcesManager_cpp destructor");
167 }
168
169 //=============================================================================
170 //! get the list of resource names fitting constraints given by params
171 /*!
172  * Steps:
173  * 1: Restrict list with resourceList if defined
174  * 2: If name is defined -> check resource list
175  * 3: If not 2:, if hostname is defined -> check resource list
176  * 4: If not 3:, sort resource with nb_proc, etc...
177  * 5: In all cases remove resource that does not correspond with OS
178  * 6: And remove resource with componentList - if list is empty ignored it...
179  */ 
180 //=============================================================================
181
182 std::vector<std::string> 
183 ResourcesManager_cpp::GetFittingResources(const resourceParams& params) throw(ResourcesException)
184 {
185   RES_MESSAGE("[GetFittingResources] on computer " << Kernel_Utils::GetHostname().c_str());
186   RES_MESSAGE("[GetFittingResources] with resource name: " << params.name);
187   RES_MESSAGE("[GetFittingResources] with hostname: "<< params.hostname);
188
189   // Result
190   std::vector<std::string> vec;
191
192   // Parse Again CalatogResource File
193   ParseXmlFiles();
194
195   // Steps:
196   // 1: If name is defined -> check resource list
197   // 2: Restrict list with resourceList if defined
198   // 3: If not 2:, if hostname is defined -> check resource list
199   // 4: If not 3:, sort resource with nb_proc, etc...
200   // 5: In all cases remove resource that does not correspond with OS
201   // 6: And remove resource with componentList - if list is empty ignored it...
202
203   // Step 1
204   if (params.name != "")
205   {
206     RES_MESSAGE("[GetFittingResources] name parameter found !");
207     if (_resourcesList.find(params.name) != _resourcesList.end())
208     {
209       vec.push_back(params.name);
210       return vec;
211     }
212     else
213       RES_MESSAGE("[GetFittingResources] resource name was not found on resource list ! name requested was " << params.name);
214       std::string error("[GetFittingResources] resource name was not found on resource list ! name requested was " + params.name);
215       throw ResourcesException(error);
216   }
217
218   MapOfParserResourcesType local_resourcesList = _resourcesList;
219   // Step 2
220   if (params.resourceList.size() > 0)
221   {
222     RES_MESSAGE("[GetFittingResources] Restricted resource list found !");
223     local_resourcesList.clear();
224     std::vector<std::string>::size_type sz = params.resourceList.size();
225
226     for (unsigned int i=0; i < sz; i++)
227     {
228       if (_resourcesList.find(params.resourceList[i]) != _resourcesList.end())
229         local_resourcesList[params.resourceList[i]] = _resourcesList[params.resourceList[i]];
230     }
231   }
232
233   // Step 3
234   if (params.hostname != "")
235   {
236     RES_MESSAGE("[GetFittingResources] Entering in hostname case !");
237
238     std::string hostname = params.hostname;
239     if (hostname ==  "localhost")
240       hostname = Kernel_Utils::GetHostname().c_str();
241
242     std::map<std::string, ParserResourcesType>::const_iterator iter = _resourcesList.begin();
243     for (; iter != _resourcesList.end(); iter++)
244     {
245       if ((*iter).second.HostName == hostname)
246         vec.push_back((*iter).first);
247     }
248   }
249   // Step 4
250   else
251   {
252     // --- Search for available resources sorted by priority
253     MapOfParserResourcesType_it i = local_resourcesList.begin();
254     for (; i != local_resourcesList.end(); ++i)
255       vec.push_back(i->first);
256
257     // --- set wanted parameters
258     ResourceDataToSort::_nbOfProcWanted = params.nb_proc;
259     ResourceDataToSort::_nbOfNodesWanted = params.nb_node;
260     ResourceDataToSort::_nbOfProcPerNodeWanted = params.nb_proc_per_node;
261     ResourceDataToSort::_CPUFreqMHzWanted = params.cpu_clock;
262     ResourceDataToSort::_memInMBWanted = params.mem_mb;
263     // --- end of set
264         
265     // Sort
266     std::list<ResourceDataToSort> li;
267     std::vector<std::string>::iterator iter = vec.begin();
268     for (; iter != vec.end(); iter++)
269       li.push_back(local_resourcesList[(*iter)].DataForSort);
270     li.sort();
271
272     vec.clear();
273     for (std::list<ResourceDataToSort>::iterator iter2 = li.begin(); iter2 != li.end(); iter2++)
274       vec.push_back((*iter2)._Name);
275   }
276
277   // Step 5
278   SelectOnlyResourcesWithOS(vec, params.OS.c_str());
279
280   // Step 6
281   std::vector<std::string> vec_save(vec);
282   KeepOnlyResourcesWithComponent(vec, params.componentList);
283   if (vec.size() == 0)
284     vec = vec_save;
285
286   // Step 7 : Filter on possible usage
287   vector<string> prev_list(vec);
288   vec.clear();
289   for (vector<string>::iterator iter = prev_list.begin() ; iter != prev_list.end() ; iter++)
290   {
291     MapOfParserResourcesType::const_iterator it = _resourcesList.find(*iter);
292     if (it != _resourcesList.end() &&
293         (!params.can_launch_batch_jobs || it->second.can_launch_batch_jobs) &&
294         (!params.can_run_containers || it->second.can_run_containers))
295       vec.push_back(*iter);
296   }
297
298   // End
299   // Send an exception if return list is empty...
300   if (vec.size() == 0)
301   {
302     std::string error("[GetFittingResources] ResourcesManager doesn't find any resource that fits to your parameters");
303     throw ResourcesException(error);
304   }
305
306   return vec;
307 }
308
309 //=============================================================================
310 /*!
311  *  add an entry in the ressources catalog xml file.
312  */ 
313 //=============================================================================
314
315 void
316 ResourcesManager_cpp::AddResourceInCatalog(const ParserResourcesType & new_resource)
317 {
318   if (new_resource.Name == DEFAULT_RESOURCE_NAME)
319     throw SALOME_Exception((string("Cannot modify default local resource \"") +
320                             DEFAULT_RESOURCE_NAME + "\"").c_str());
321   // TODO - Add minimal check
322   _resourcesList[new_resource.Name] = new_resource;
323 }
324
325 //=============================================================================
326 /*!
327  *  Deletes a resource from the catalog
328  */ 
329 //=============================================================================
330
331 void ResourcesManager_cpp::DeleteResourceInCatalog(const char * name)
332 {
333   if (DEFAULT_RESOURCE_NAME == name)
334     throw SALOME_Exception((string("Cannot delete default local resource \"") +
335                             DEFAULT_RESOURCE_NAME + "\"").c_str());
336   MapOfParserResourcesType_it it = _resourcesList.find(name);
337   if (it != _resourcesList.end())
338     _resourcesList.erase(name);
339   else
340     RES_INFOS("You try to delete a resource that does not exist... : " << name);
341 }
342
343 //=============================================================================
344 /*!
345  *  write the current data in memory in file.
346  */ 
347 //=============================================================================
348
349 void ResourcesManager_cpp::WriteInXmlFile(std::string xml_file)
350 {
351   RES_MESSAGE("WriteInXmlFile : start");
352
353   MapOfParserResourcesType resourceListToSave(_resourcesList);
354   // We do not save default local resource because it is automatically created at startup
355   resourceListToSave.erase(DEFAULT_RESOURCE_NAME);
356   if (resourceListToSave.empty())
357   {
358     RES_MESSAGE("WriteInXmlFile: nothing to do, no resource except default \"" <<
359                 DEFAULT_RESOURCE_NAME << "\"");
360     return;
361   }
362
363   if (xml_file == "")
364   {
365     _path_resources_it = _path_resources.begin();
366     xml_file = *_path_resources_it;
367   }
368
369   const char* aFilePath = xml_file.c_str();
370   FILE* aFile = fopen(aFilePath, "w");
371
372   if (aFile == NULL)
373   {
374     std::cerr << "Error opening file in WriteInXmlFile : " << xml_file << std::endl;
375     return;
376   }
377   
378   xmlDocPtr aDoc = xmlNewDoc(BAD_CAST "1.0");
379   xmlNewDocComment(aDoc, BAD_CAST "ResourcesCatalog");
380
381   SALOME_ResourcesCatalog_Handler* handler =
382     new SALOME_ResourcesCatalog_Handler(resourceListToSave);
383   handler->PrepareDocToXmlFile(aDoc);
384   delete handler;
385
386   int isOk = xmlSaveFormatFile(aFilePath, aDoc, 1);
387   if (!isOk) 
388      std::cerr << "Error while XML file saving : " << xml_file << std::endl;
389   
390   // Free the document
391   xmlFreeDoc(aDoc);
392   fclose(aFile);
393   RES_MESSAGE("WriteInXmlFile : WRITING DONE!");
394 }
395
396 //=============================================================================
397 /*!
398  *  parse the data type catalog
399  */ 
400 //=============================================================================
401
402 const MapOfParserResourcesType& ResourcesManager_cpp::ParseXmlFiles()
403 {
404   // Parse file only if its modification time is greater than lasttime (last registered modification time)
405   bool to_parse = false;
406   for(_path_resources_it = _path_resources.begin(); _path_resources_it != _path_resources.end(); ++_path_resources_it)
407   {
408     struct stat statinfo;
409     int result = stat((*_path_resources_it).c_str(), &statinfo);
410     if (result < 0)
411     {
412       RES_MESSAGE("Resource file " << *_path_resources_it << " does not exist");
413       return _resourcesList;
414     }
415
416     if(statinfo.st_mtime > _lasttime)
417     {
418       to_parse = true;
419       _lasttime = statinfo.st_mtime;
420     }
421   }
422
423   if (to_parse)
424   {
425     _resourcesList.clear();
426     AddDefaultResourceInCatalog();
427     // On parse tous les fichiers
428     for(_path_resources_it = _path_resources.begin(); _path_resources_it != _path_resources.end(); ++_path_resources_it)
429     {
430       MapOfParserResourcesType _resourcesList_tmp;
431       MapOfParserResourcesType _resourcesBatchList_tmp;
432       SALOME_ResourcesCatalog_Handler* handler =
433         new SALOME_ResourcesCatalog_Handler(_resourcesList_tmp);
434       const char* aFilePath = (*_path_resources_it).c_str();
435       FILE* aFile = fopen(aFilePath, "r");
436
437       if (aFile != NULL)
438       {
439         xmlDocPtr aDoc = xmlReadFile(aFilePath, NULL, 0);
440         if (aDoc != NULL)
441         {
442           handler->ProcessXmlDocument(aDoc);
443
444           // adding new resources to the file
445           for (MapOfParserResourcesType_it i = _resourcesList_tmp.begin(); i != _resourcesList_tmp.end(); ++i)
446           {
447             MapOfParserResourcesType_it j = _resourcesList.find(i->first);
448             if (i->second.HostName == "localhost" || i->second.HostName == Kernel_Utils::GetHostname())
449             {
450               RES_MESSAGE("Resource " << i->first << " is not added because it is the same "
451                           "machine as default local resource \"" << DEFAULT_RESOURCE_NAME << "\"");
452             }
453             else if (j != _resourcesList.end())
454             {
455               cerr << "ParseXmlFiles Warning, two resources with the same name were found, "
456                       "taking the first declaration : " << i->first << endl;
457             }
458             else
459             {
460               _resourcesList[i->first] = i->second;
461             }
462           }
463         }
464         else
465           std::cerr << "ResourcesManager_cpp: could not parse file " << aFilePath << std::endl;
466         // Free the document
467         xmlFreeDoc(aDoc);
468         fclose(aFile);
469       }
470       else
471         std::cerr << "ResourcesManager_cpp: file " << aFilePath << " is not readable." << std::endl;
472
473       delete handler;
474     }
475   }
476   return _resourcesList;
477 }
478
479 //=============================================================================
480 /*!
481  *   consult the content of the list
482  */ 
483 //=============================================================================
484
485 const MapOfParserResourcesType& ResourcesManager_cpp::GetList() const
486 {
487   return _resourcesList;
488 }
489
490 std::string ResourcesManager_cpp::Find(const std::string& policy, const std::vector<std::string>& listOfResources)
491 {
492   if(_resourceManagerMap.count(policy)==0)
493     return _resourceManagerMap[""]->Find(listOfResources, _resourcesList);
494   return _resourceManagerMap[policy]->Find(listOfResources, _resourcesList);
495 }
496
497 //=============================================================================
498 /*!
499  *  Gives a sublist of resources with matching OS.
500  *  If parameter OS is empty, gives the complete list of resources
501  */ 
502 //=============================================================================
503 void 
504 ResourcesManager_cpp::SelectOnlyResourcesWithOS(std::vector<std::string>& resources, std::string OS)
505 {
506   if (OS != "")
507   {
508     // a computer list is given : take only resources with OS on those computers
509     std::vector<std::string> vec_tmp = resources;
510     resources.clear();
511     std::vector<std::string>::iterator iter = vec_tmp.begin();
512     for (; iter != vec_tmp.end(); iter++)
513     {
514       MapOfParserResourcesType::const_iterator it = _resourcesList.find(*iter);
515       if(it != _resourcesList.end())
516         if ( (*it).second.OS == OS)
517           resources.push_back(*iter);
518     }
519   }
520 }
521
522
523 //=============================================================================
524 /*!
525  *  Gives a sublist of machines on which the component is known.
526  */ 
527 //=============================================================================
528 void 
529 ResourcesManager_cpp::KeepOnlyResourcesWithComponent(std::vector<std::string>& resources, 
530                                                      const std::vector<std::string>& componentList)
531 {
532   std::vector<std::string> kept_resources;
533
534   std::vector<std::string>::iterator iter = resources.begin();
535   for (; iter != resources.end(); iter++)
536   {
537     const std::vector<std::string>& mapOfComponentsOfCurrentHost = _resourcesList[*iter].ComponentsList;
538
539     bool erasedHost = false;
540     if( mapOfComponentsOfCurrentHost.size() > 0 )
541     {
542       for(unsigned int i=0; i<componentList.size(); i++)
543       {
544         std::vector<std::string>::const_iterator itt = find(mapOfComponentsOfCurrentHost.begin(),
545                                                             mapOfComponentsOfCurrentHost.end(),
546                                                             componentList[i]);
547         if (itt == mapOfComponentsOfCurrentHost.end())
548         {
549           erasedHost = true;
550           break;
551         }
552       }
553     }
554     if(!erasedHost)
555       kept_resources.push_back(*iter);
556   }
557   resources=kept_resources;
558 }
559
560
561 ParserResourcesType 
562 ResourcesManager_cpp::GetResourcesDescr(const std::string & name)
563 {
564   if (_resourcesList.find(name) != _resourcesList.end())
565     return _resourcesList[name];
566   else
567   {
568     std::string error("[GetResourcesDescr] Resource does not exist: ");
569     error += name;
570     throw ResourcesException(error);
571   }
572 }
573
574 void ResourcesManager_cpp::AddDefaultResourceInCatalog()
575 {
576   ParserResourcesType resource;
577   resource.Name = DEFAULT_RESOURCE_NAME;
578   // We can't use "localhost" for parameter hostname because the containers are registered in the
579   // naming service with the real hostname, not "localhost"
580   resource.HostName = Kernel_Utils::GetHostname();
581   resource.DataForSort._Name = DEFAULT_RESOURCE_NAME;
582   resource.Protocol = sh;
583   resource.Batch = ssh_batch;
584   if (getenv("HOME") != NULL && getenv("APPLI") != NULL)
585   {
586     resource.AppliPath = string(getenv("HOME")) + "/" + getenv("APPLI");
587   }
588   resource.working_directory = "/tmp/salome_localres_workdir";
589   resource.can_launch_batch_jobs = true;
590   resource.can_run_containers = true;
591   _resourcesList[resource.Name] = resource;
592 }