1 // Copyright (C) 2005 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
2 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License.
9 // This library is distributed in the hope that it will be useful
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 // See http://www.salome-platform.org/
20 #include "SALOME_ResourcesManager.hxx"
21 //#include "SALOME_Container_i.hxx"
22 #include "Utils_ExceptHandlers.hxx"
41 #include <sys/types.h>
43 #include "utilities.h"
45 #define MAX_SIZE_FOR_HOSTNAME 256;
49 //=============================================================================
53 //=============================================================================
55 SALOME_ResourcesManager::
56 SALOME_ResourcesManager(CORBA::ORB_ptr orb,
57 const char *xmlFilePath) :
58 _path_resources(xmlFilePath)
60 _NS = new SALOME_NamingService(orb);
63 //=============================================================================
65 * Standard constructor, parse resource file.
66 * - if ${APPLI} exists in environment,
67 * look for ${HOME}/*{APPLI}/CatalogResources.xml
68 * - else look for default:
69 * ${KERNEL_ROOT_DIR}/share/salome/resources/CatalogResources.xml
70 * - parse XML resource file.
72 //=============================================================================
74 SALOME_ResourcesManager::SALOME_ResourcesManager(CORBA::ORB_ptr orb)
76 _NS = new SALOME_NamingService(orb);
77 _isAppliSalomeDefined = (getenv("APPLI") != 0);
79 if (_isAppliSalomeDefined)
81 _path_resources = getenv("HOME");
82 _path_resources += "/";
83 _path_resources += getenv("APPLI");
84 _path_resources += "/CatalogResources.xml";
89 _path_resources = getenv("KERNEL_ROOT_DIR");
90 _path_resources += "/share/salome/resources/CatalogResources.xml";
96 //=============================================================================
100 //=============================================================================
102 SALOME_ResourcesManager::~SALOME_ResourcesManager()
107 //=============================================================================
109 * get the list of name of ressources fitting for the specified module.
110 * If hostname specified, check it is local or known in resources catalog.
113 * - select first machines with corresponding OS (all machines if
114 * parameter OS empty),
115 * - then select the sublist of machines on witch the module is known
116 * (if the result is empty, that probably means that the inventory of
117 * modules is probably not done, so give complete list from previous step)
119 //=============================================================================
122 SALOME_ResourcesManager::
123 GetFittingResources(const Engines::MachineParameters& params,
124 const char *moduleName)
125 throw(SALOME_Exception)
127 MESSAGE("ResourcesManager::GetFittingResources");
128 vector <std::string> ret;
130 // --- To be sure that we search in a correct list.
133 const char *hostname = (const char *)params.hostname;
134 MESSAGE("GetFittingResources " << hostname << " " << GetHostname().c_str());
136 if (hostname[0] != '\0')
138 MESSAGE("ResourcesManager::GetFittingResources : hostname specified" );
140 if ( strcmp(hostname, "localhost") == 0 ||
141 strcmp(hostname, GetHostname().c_str()) == 0 )
143 MESSAGE("ResourcesManager::GetFittingResources : localhost" );
144 ret.push_back(GetHostname().c_str());
145 MESSAGE("ResourcesManager::GetFittingResources : " << ret.size());
148 else if (_resourcesList.find(hostname) != _resourcesList.end())
150 // --- params.hostname is in the list of resources so return it.
151 ret.push_back(hostname);
156 // --- user specified an unknown hostame so notify him.
157 MESSAGE("ResourcesManager::GetFittingResources : SALOME_Exception");
158 throw SALOME_Exception("unknown host");
163 // --- Search for available resources sorted by priority
165 SelectOnlyResourcesWithOS(ret, params.OS);
167 KeepOnlyResourcesWithModule(ret, moduleName);
170 SelectOnlyResourcesWithOS(ret, params.OS);
172 // --- set wanted parameters
173 ResourceDataToSort::_nbOfNodesWanted = params.nb_node;
175 ResourceDataToSort::_nbOfProcPerNodeWanted = params.nb_proc_per_node;
177 ResourceDataToSort::_CPUFreqMHzWanted = params.cpu_clock;
179 ResourceDataToSort::_memInMBWanted = params.mem_mb;
183 list<ResourceDataToSort> li;
185 for (vector<string>::iterator iter = ret.begin();
188 li.push_back(_resourcesList[(*iter)].DataForSort);
194 for (list<ResourceDataToSort>::iterator iter2 = li.begin();
197 ret[i++] = (*iter2)._hostName;
200 MESSAGE("ResourcesManager::GetFittingResources : return" << ret.size());
204 //=============================================================================
206 * add an entry in the ressources catalog xml file.
207 * Return 0 if OK (KERNEL found in new resources modules) else throw exception
209 //=============================================================================
212 SALOME_ResourcesManager::
213 AddResourceInCatalog(const Engines::MachineParameters& paramsOfNewResources,
214 const map<string, string>& modulesOnNewResources,
215 const char *environPathOfPrerequired,
217 const char *userName,
219 AccessProtocolType prot)
220 throw(SALOME_Exception)
222 map<string, string>::const_iterator iter =
223 modulesOnNewResources.find("KERNEL");
225 if (iter != modulesOnNewResources.end())
227 ParserResourcesType newElt;
228 newElt.DataForSort._hostName = paramsOfNewResources.hostname;
229 newElt.Alias = alias;
230 newElt.Protocol = prot;
232 newElt.UserName = userName;
233 newElt.ModulesPath = modulesOnNewResources;
234 newElt.PreReqFilePath = environPathOfPrerequired;
235 newElt.OS = paramsOfNewResources.OS;
236 newElt.DataForSort._memInMB = paramsOfNewResources.mem_mb;
237 newElt.DataForSort._CPUFreqMHz = paramsOfNewResources.cpu_clock;
238 newElt.DataForSort._nbOfNodes = paramsOfNewResources.nb_node;
239 newElt.DataForSort._nbOfProcPerNode =
240 paramsOfNewResources.nb_proc_per_node;
241 _resourcesList[newElt.DataForSort._hostName] = newElt;
246 throw SALOME_Exception("KERNEL is not present in this resource");
249 //=============================================================================
251 * Deletes a resource from the catalog
253 //=============================================================================
255 void SALOME_ResourcesManager::DeleteResourceInCatalog(const char *hostname)
257 _resourcesList.erase(hostname);
260 //=============================================================================
262 * write the current data in memory in file.
264 //=============================================================================
266 void SALOME_ResourcesManager::WriteInXmlFile()
268 QDomDocument doc("ResourcesCatalog");
269 SALOME_ResourcesCatalog_Handler* handler =
270 new SALOME_ResourcesCatalog_Handler(_resourcesList);
271 handler->PrepareDocToXmlFile(doc);
274 QFile file( _path_resources );
276 if ( !file.open( IO_WriteOnly ) )
277 INFOS("WRITING ERROR !");
279 QTextStream ts( &file );
281 ts << doc.toString();
285 MESSAGE("WRITING DONE!");
288 //=============================================================================
290 * parse the data type catalog
292 //=============================================================================
294 const MapOfParserResourcesType& SALOME_ResourcesManager::ParseXmlFile()
296 SALOME_ResourcesCatalog_Handler* handler =
297 new SALOME_ResourcesCatalog_Handler(_resourcesList);
298 QFile xmlFile(_path_resources);
300 QXmlInputSource source(xmlFile);
302 QXmlSimpleReader reader;
303 reader.setContentHandler( handler );
304 reader.setErrorHandler( handler );
305 reader.parse( source );
308 return _resourcesList;
311 //=============================================================================
313 * consult the content of the list
315 //=============================================================================
317 const MapOfParserResourcesType& SALOME_ResourcesManager::GetList() const
319 return _resourcesList;
323 //=============================================================================
325 * dynamically obtains the best machines
327 //=============================================================================
330 SALOME_ResourcesManager::FindBest(const Engines::MachineList& listOfMachines)
332 return _dynamicResourcesSelecter.FindBest(listOfMachines);
336 //=============================================================================
338 * This is no longer valid (C++ container are also python containers)
340 //=============================================================================
342 bool isPythonContainer(const char* ContainerName)
345 int len = strlen(ContainerName);
348 if (strcmp(ContainerName + len - 2, "Py") == 0)
355 //=============================================================================
357 * Builds the script to be launched
359 * If SALOME Application not defined ($APPLI),
360 * see BuildTempFileToLaunchRemoteContainer()
362 * Else rely on distant configuration. Command is under the form (example):
363 * ssh user@machine distantPath/runRemote.sh hostNS portNS \
364 * SALOME_Container containerName &"
366 * - where user is ommited if not specified in CatalogResources,
367 * - where distant path is always relative to user@machine $HOME, and
368 * equal to $APPLI if not specified in CatalogResources,
369 * - where hostNS is the hostname of CORBA naming server (set by scripts to
370 * use to launch SALOME and servers in $APPLI: runAppli.sh, runRemote.sh)
371 * - where portNS is the port used by CORBA naming server (set by scripts to
372 * use to launch SALOME and servers in $APPLI: runAppli.sh, runRemote.sh)
374 //=============================================================================
377 SALOME_ResourcesManager::BuildCommandToLaunchRemoteContainer
378 (const string& machine,
379 const Engines::MachineParameters& params, const long id)
383 char idc[3*sizeof(long)];
385 if ( ! _isAppliSalomeDefined )
386 command = BuildTempFileToLaunchRemoteContainer(machine, params);
390 const ParserResourcesType& resInfo = _resourcesList[machine];
394 if ( (params.nb_node <= 0) && (params.nb_proc_per_node <= 0) )
396 else if ( params.nb_node == 0 )
397 nbproc = params.nb_proc_per_node;
398 else if ( params.nb_proc_per_node == 0 )
399 nbproc = params.nb_node;
401 nbproc = params.nb_node * params.nb_proc_per_node;
404 // "ssh user@machine distantPath/runRemote.sh hostNS portNS \
405 // SALOME_Container containerName &"
407 if (resInfo.Protocol == rsh)
409 else if (resInfo.Protocol == ssh)
412 throw SALOME_Exception("Unknown protocol");
414 if (resInfo.UserName != "")
416 command += resInfo.UserName;
423 if (resInfo.AppliPath != "")
424 command += resInfo.AppliPath; // path relative to user@machine $HOME
427 ASSERT(getenv("APPLI"));
428 command += getenv("APPLI"); // path relative to user@machine $HOME
431 command += "/runRemote.sh ";
433 ASSERT(getenv("NSHOST"));
434 command += getenv("NSHOST"); // hostname of CORBA name server
437 ASSERT(getenv("NSPORT"));
438 command += getenv("NSPORT"); // port of CORBA name server
442 command += " mpirun -np ";
443 std::ostringstream o;
447 command += "-x PATH,LD_LIBRARY_PATH,OMNIORB_CONFIG,SALOME_trace ";
449 command += " SALOME_MPIContainer ";
452 command += " SALOME_Container ";
454 command += _NS->ContainerName(params);
456 sprintf(idc,"%ld",id);
459 AddOmninamesParams(command);
460 command += " > /tmp/";
461 command += _NS->ContainerName(params);
463 command += GetHostname();
465 command += getenv( "USER" ) ;
466 command += ".log 2>&1 &" ;
468 MESSAGE("command =" << command);
475 //=============================================================================
477 * builds the command to be launched.
479 //=============================================================================
482 SALOME_ResourcesManager::BuildCommandToLaunchLocalContainer
483 (const Engines::MachineParameters& params, const long id)
488 char idc[3*sizeof(long)];
492 command = "mpirun -np ";
494 if ( (params.nb_node <= 0) && (params.nb_proc_per_node <= 0) )
496 else if ( params.nb_node == 0 )
497 nbproc = params.nb_proc_per_node;
498 else if ( params.nb_proc_per_node == 0 )
499 nbproc = params.nb_node;
501 nbproc = params.nb_node * params.nb_proc_per_node;
503 std::ostringstream o;
509 command += "-x PATH,LD_LIBRARY_PATH,OMNIORB_CONFIG,SALOME_trace ";
512 if (isPythonContainer(params.container_name))
513 command += "pyMPI SALOME_ContainerPy.py ";
515 command += "SALOME_MPIContainer ";
520 if (isPythonContainer(params.container_name))
521 command = "SALOME_ContainerPy.py ";
523 command = "SALOME_Container ";
526 command += _NS->ContainerName(params);
528 sprintf(idc,"%ld",id);
531 AddOmninamesParams(command);
532 command += " > /tmp/";
533 command += _NS->ContainerName(params);
535 command += GetHostname();
537 command += getenv( "USER" ) ;
538 command += ".log 2>&1 &" ;
539 MESSAGE("Command is ... " << command);
544 //=============================================================================
546 * removes the generated temporary file in case of a remote launch.
548 //=============================================================================
550 void SALOME_ResourcesManager::RmTmpFile()
552 if (_TmpFileName != "")
554 string command = "rm ";
555 command += _TmpFileName;
556 char *temp = strdup(command.c_str());
557 int lgthTemp = strlen(temp);
558 temp[lgthTemp - 3] = '*';
559 temp[lgthTemp - 2] = '\0';
566 //=============================================================================
568 * builds the script to be launched
570 //=============================================================================
573 SALOME_ResourcesManager::BuildCommand
574 (const string& machine,
575 const char *containerName)
577 // rsh -n ikkyo /export/home/rahuel/SALOME_ROOT/bin/runSession SALOME_Container -ORBInitRef NameService=corbaname::dm2s0017:1515 &
578 const ParserResourcesType& resInfo = _resourcesList[machine];
579 bool pyCont = isPythonContainer(containerName);
583 if (resInfo.Protocol == rsh)
584 command = "rsh -n " ;
585 else if (resInfo.Protocol == ssh)
586 command = "ssh -f -n ";
588 throw SALOME_Exception("Not implemented yet...");
592 string path = (*(resInfo.ModulesPath.find("KERNEL"))).second;
594 command += "/bin/salome/";
597 command += "SALOME_ContainerPy.py ";
599 command += "SALOME_Container ";
601 command += containerName;
603 AddOmninamesParams(command);
604 command += " > /tmp/";
605 command += containerName;
608 command += ".log 2>&1 &" ;
614 //=============================================================================
616 * Gives a sublist of machines with matching OS.
617 * If parameter OS is empty, gives the complete list of machines
619 //=============================================================================
621 // Warning need an updated parsed list : _resourcesList
623 SALOME_ResourcesManager::SelectOnlyResourcesWithOS
624 ( vector<string>& hosts,
625 const char *OS) const
626 throw(SALOME_Exception)
630 for (map<string, ParserResourcesType>::const_iterator iter =
631 _resourcesList.begin();
632 iter != _resourcesList.end();
635 if ( (*iter).second.OS == base || base.size() == 0)
636 hosts.push_back((*iter).first);
641 //=============================================================================
643 * Gives a sublist of machines on which the module is known.
645 //=============================================================================
647 //Warning need an updated parsed list : _resourcesList
649 SALOME_ResourcesManager::KeepOnlyResourcesWithModule
650 ( vector<string>& hosts,
651 const char *moduleName) const
652 throw(SALOME_Exception)
654 for (vector<string>::iterator iter = hosts.begin(); iter != hosts.end();)
656 MapOfParserResourcesType::const_iterator it = _resourcesList.find(*iter);
657 const map<string, string>& mapOfModulesOfCurrentHost =
658 (((*it).second).ModulesPath);
660 if (mapOfModulesOfCurrentHost.find(moduleName) ==
661 mapOfModulesOfCurrentHost.end())
669 //=============================================================================
671 * add to command all options relative to naming service.
673 //=============================================================================
675 void SALOME_ResourcesManager::AddOmninamesParams(string& command) const
677 // If env variable OMNIORB_CONFIG is not defined or the file is more complex than one line
679 // Even if we use it we have to check if env variable exists
680 //string omniORBcfg( getenv( "OMNIORB_CONFIG" ) ) ;
681 //ifstream omniORBfile( omniORBcfg.c_str() ) ;
682 //char ORBInitRef[11] ;
684 //char nameservice[132] ;
685 //omniORBfile >> ORBInitRef ;
686 //command += "ORBInitRef " ;
687 //omniORBfile >> egal ;
688 //omniORBfile >> nameservice ;
689 //omniORBfile.close() ;
690 //char * bsn = strchr( nameservice , '\n' ) ;
694 //command += nameservice ;
696 char *iorstr = _NS->getIORaddr();
697 command += "ORBInitRef NameService=";
702 //=============================================================================
704 * add to command all options relative to naming service.
706 //=============================================================================
708 void SALOME_ResourcesManager::AddOmninamesParams(ofstream& fileStream) const
710 string omniORBcfg( getenv( "OMNIORB_CONFIG" ) ) ;
711 ifstream omniORBfile( omniORBcfg.c_str() ) ;
712 char ORBInitRef[11] ;
714 char nameservice[132] ;
715 omniORBfile >> ORBInitRef ;
716 fileStream << "ORBInitRef ";
717 omniORBfile >> egal ;
718 omniORBfile >> nameservice ;
719 omniORBfile.close() ;
720 char * bsn = strchr( nameservice , '\n' ) ;
727 fileStream << nameservice;
731 //=============================================================================
733 * generate a file name in /tmp directory
735 //=============================================================================
737 string SALOME_ResourcesManager::BuildTemporaryFileName() const
739 //build more complex file name to support multiple salome session
740 char *temp = new char[19];
741 strcpy(temp, "/tmp/command");
742 strcat(temp, "XXXXXX");
749 itoa(getpid(), aPID, 10);
753 string command(temp);
760 //=============================================================================
762 * Builds in a temporary file the script to be launched.
764 * Used if SALOME Application ($APPLI) is not defined.
765 * The command is build with data from CatalogResources, in which every path
766 * used on remote computer must be defined.
768 //=============================================================================
771 SALOME_ResourcesManager::BuildTempFileToLaunchRemoteContainer
772 (const string& machine,
773 const Engines::MachineParameters& params)
775 _TmpFileName = BuildTemporaryFileName();
776 ofstream tempOutputFile;
777 tempOutputFile.open(_TmpFileName.c_str(), ofstream::out );
778 const ParserResourcesType& resInfo = _resourcesList[machine];
779 tempOutputFile << "#! /bin/sh" << endl;
783 for (map<string, string>::const_iterator iter = resInfo.ModulesPath.begin();
784 iter != resInfo.ModulesPath.end();
787 string curModulePath((*iter).second);
788 tempOutputFile << (*iter).first << "_ROOT_DIR=" << curModulePath << endl;
789 tempOutputFile << "export " << (*iter).first << "_ROOT_DIR" << endl;
790 tempOutputFile << "LD_LIBRARY_PATH=" << curModulePath
791 << "/lib/salome" << ":${LD_LIBRARY_PATH}" << endl;
792 tempOutputFile << "PYTHONPATH=" << curModulePath << "/bin/salome:"
793 << curModulePath << "/lib/salome:" << curModulePath
794 << "/lib/python2.2/site-packages/salome:";
795 tempOutputFile << curModulePath
796 << "/lib/python2.2/site-packages/salome/shared_modules:${PYTHONPATH}"
800 tempOutputFile << "export LD_LIBRARY_PATH" << endl;
801 tempOutputFile << "export PYTHONPATH" << endl;
802 tempOutputFile << "source " << resInfo.PreReqFilePath << endl;
808 tempOutputFile << "mpirun -np ";
811 if ( (params.nb_node <= 0) && (params.nb_proc_per_node <= 0) )
813 else if ( params.nb_node == 0 )
814 nbproc = params.nb_proc_per_node;
815 else if ( params.nb_proc_per_node == 0 )
816 nbproc = params.nb_node;
818 nbproc = params.nb_node * params.nb_proc_per_node;
820 std::ostringstream o;
822 tempOutputFile << nbproc << " ";
824 tempOutputFile << "-x PATH,LD_LIBRARY_PATH,OMNIORB_CONFIG,SALOME_trace ";
828 tempOutputFile << (*(resInfo.ModulesPath.find("KERNEL"))).second
833 if (isPythonContainer(params.container_name))
834 tempOutputFile << "pyMPI SALOME_ContainerPy.py ";
836 tempOutputFile << "SALOME_MPIContainer ";
841 if (isPythonContainer(params.container_name))
842 tempOutputFile << "SALOME_ContainerPy.py ";
844 tempOutputFile << "SALOME_Container ";
847 tempOutputFile << _NS->ContainerName(params) << " -";
848 AddOmninamesParams(tempOutputFile);
849 tempOutputFile << " &" << endl;
850 tempOutputFile.flush();
851 tempOutputFile.close();
852 chmod(_TmpFileName.c_str(), 0x1ED);
858 if (resInfo.Protocol == rsh)
861 string commandRcp = "rcp ";
862 commandRcp += _TmpFileName;
864 commandRcp += machine;
866 commandRcp += _TmpFileName;
867 system(commandRcp.c_str());
870 else if (resInfo.Protocol == ssh)
873 throw SALOME_Exception("Unknown protocol");
876 _CommandForRemAccess = command;
878 command += _TmpFileName;
881 command += _NS->ContainerName(params);
884 command += ".log 2>&1 &";