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