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