Salome HOME
Copyright update 2021
[modules/yacs.git] / src / runtime / SalomeContainerTools.cxx
1 // Copyright (C) 2006-2021  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 #define _DEVDEBUG_
20 #include "SalomeContainerTools.hxx"
21 #include "SALOME_LifeCycleCORBA.hxx"
22 #include "SALOME_NamingService.hxx"
23 #include "SALOME_ResourcesManager.hxx"
24 #include "SALOME_ContainerManager.hxx"
25 #include "Container.hxx"
26 #include "AutoLocker.hxx"
27
28 #include "YacsTrace.hxx"
29 #include "Proc.hxx"
30 #include "ServiceNode.hxx"
31 #include "ComponentInstance.hxx"
32 #include "SalomeContainerHelper.hxx"
33 #include "SalomeHPContainerTools.hxx"
34 #include "RuntimeSALOME.hxx"
35 #include "Exception.hxx"
36
37 #include <sstream>
38
39 #ifdef WIN32
40 #include <process.h>
41 #define getpid _getpid
42 #endif
43
44 using namespace YACS::ENGINE;
45
46 SalomeContainerTools::SalomeContainerTools()
47 {
48   /* Init ContainerParameters */
49   SALOME_LifeCycleCORBA::preSet(_params);
50 }
51
52 SalomeContainerTools::SalomeContainerTools(const SalomeContainerTools& other):_params(other._params),_propertyMap(other._propertyMap)
53 {
54 }
55
56 void SalomeContainerTools::clearProperties()
57 {
58   _propertyMap.clear();
59   _params=Engines::ContainerParameters();
60 }
61
62 std::string SalomeContainerTools::getProperty(const std::string& name) const
63 {
64   std::map<std::string,std::string>::const_iterator it(_propertyMap.find(name));
65   if(it!=_propertyMap.end())
66     return (*it).second;
67   else
68     return std::string();
69 }
70
71 void SalomeContainerTools::setProperty(const std::string& name, const std::string& value)
72 {
73   //DEBTRACE("SalomeContainer::setProperty : " << name << " ; " << value);
74   // Container Part
75   if (name == "container_name")
76     _params.container_name = CORBA::string_dup(value.c_str());
77   else if (name == "workingdir")
78     _params.workingdir = CORBA::string_dup(value.c_str());
79   else if (name == "nb_parallel_procs")
80   {
81     std::istringstream iss(value);
82     if (!(iss >> _params.nb_proc))
83       throw Exception("salomecontainer::setproperty : params.nb_proc value not correct : " + value);
84   }
85   else if (name == "isMPI")
86   {
87     if (value == "true")
88       _params.isMPI = true;
89     else if (value == "false")
90       _params.isMPI = false;
91     else 
92       throw Exception("SalomeContainer::setProperty : params.isMPI value not correct : " + value);
93   }
94   else if (name == "parallelLib")
95     _params.parallelLib = CORBA::string_dup(value.c_str());
96
97   // Resource part
98   else if (name == "name")
99     _params.resource_params.name = CORBA::string_dup(value.c_str());
100   else if (name == "hostname")
101     _params.resource_params.hostname = CORBA::string_dup(value.c_str());
102   else if (name == "OS")
103     _params.resource_params.OS = CORBA::string_dup(value.c_str());
104   else if (name == "nb_resource_procs")
105   {
106     std::istringstream iss(value);
107     if (!(iss >> _params.resource_params.nb_proc))
108       throw Exception("salomecontainer::setproperty : params.resource_params.nb_proc value not correct : " + value);
109   }
110   else if (name == "mem_mb")
111   {
112     std::istringstream iss(value);
113     if (!(iss >> _params.resource_params.mem_mb))
114       throw Exception("salomecontainer::setproperty : params.resource_params.mem_mb value not correct : " + value);
115   }
116   else if (name == "cpu_clock")
117   {
118     std::istringstream iss(value);
119     if (!(iss >> _params.resource_params.cpu_clock))
120       throw Exception("salomecontainer::setproperty : params.resource_params.cpu_clock value not correct : " + value);
121   }
122   else if (name == "nb_node")
123   {
124     std::istringstream iss(value);
125     if (!(iss >> _params.resource_params.nb_node))
126       throw Exception("salomecontainer::setproperty : params.nb_node value not correct : " + value);
127   }
128   else if (name == "nb_proc_per_node")
129   {
130     std::istringstream iss(value);
131     if (!(iss >> _params.resource_params.nb_proc_per_node))
132       throw Exception("salomecontainer::setproperty : params.nb_proc_per_node value not correct : " + value);
133   }
134   else if (name == "policy")
135     _params.resource_params.policy = CORBA::string_dup(value.c_str());
136   else if (name == "component_list")
137   {
138     std::string clean_value(value);
139
140     // Step 1: remove blanks
141     while(clean_value.find(" ") != std::string::npos)
142       clean_value = clean_value.erase(clean_value.find(" "), 1);
143
144     // Step 2: get values
145     while(!clean_value.empty())
146     {
147       std::string result("");
148       std::string::size_type loc = clean_value.find(",", 0);
149       if (loc != std::string::npos)
150       {
151         result = clean_value.substr(0, loc);
152         clean_value = clean_value.erase(0, loc+1);
153       }
154       else
155       {
156         result = clean_value;
157         clean_value.erase();
158       }
159       if (result != "," && result != "")
160       {
161         addToComponentList(result);
162       }
163     }
164
165   }
166   else if (name == "resource_list")
167   {
168     std::string clean_value(value);
169
170     // Step 1: remove blanks
171     while(clean_value.find(" ") != std::string::npos)
172       clean_value = clean_value.erase(clean_value.find(" "), 1);
173
174     // Step 2: get values
175     while(!clean_value.empty())
176     {
177       std::string result("");
178       std::string::size_type loc = clean_value.find(",", 0);
179       if (loc != std::string::npos)
180       {
181         result = clean_value.substr(0, loc);
182         clean_value = clean_value.erase(0, loc+1);
183       }
184       else
185       {
186         result = clean_value;
187         clean_value.erase();
188       }
189       if (result != "," && result != "")
190       {
191         addToResourceList(result);
192       }
193     }
194
195   }
196   _propertyMap[name]=value;
197 }
198
199 void SalomeContainerTools::addToComponentList(const std::string& name)
200 {
201   // Search if name is already in the list
202   for (CORBA::ULong i = 0; i < _params.resource_params.componentList.length(); i++)
203     {
204       std::string component_name = _params.resource_params.componentList[i].in();
205       if (component_name == name)
206         return;
207     }
208   // Add name to list
209   CORBA::ULong lgth = _params.resource_params.componentList.length();
210   _params.resource_params.componentList.length(lgth + 1);
211   _params.resource_params.componentList[lgth] = CORBA::string_dup(name.c_str());
212 }
213
214 void SalomeContainerTools::addToResourceList(const std::string& name)
215 {
216   // Search if name is already in the list
217   for (CORBA::ULong i = 0; i < _params.resource_params.resList.length(); i++)
218     {
219       std::string component_name = _params.resource_params.resList[i].in();
220       if (component_name == name)
221         return;
222     }
223   // Add name to list
224   CORBA::ULong lgth = _params.resource_params.resList.length();
225   _params.resource_params.resList.length(lgth + 1);
226   _params.resource_params.resList[lgth] = CORBA::string_dup(name.c_str());
227 }
228
229 std::string SalomeContainerTools::getContainerName() const
230 {
231   return std::string(_params.container_name);
232 }
233
234 int SalomeContainerTools::getNumberOfCoresPerWorker() const
235 {
236   return _params.resource_params.nb_proc_per_node;
237 }
238
239 void SalomeContainerTools::setContainerName(const std::string& name)
240 {
241   SetContainerNameOf(_params,name);
242 }
243
244 std::string SalomeContainerTools::getNotNullContainerName(const Container *contPtr, const Task *askingNode, bool& isEmpty) const
245 {
246   isEmpty=true;
247   std::string name(_params.container_name);
248   if(!name.empty())
249     {
250       isEmpty=false;
251       return name;
252     }
253   else
254     {
255       //give a almost unique name to the container : Pid_Name_Addr
256       std::ostringstream stream;
257       stream << getpid();
258       stream << "_";
259       stream << contPtr->getName();
260       stream << "_";
261       stream << contPtr->getDiscreminantStrOfThis(askingNode);
262       return stream.str();
263     }
264 }
265
266 std::string SalomeContainerTools::getHostName() const
267 {
268   return std::string(_params.resource_params.hostname);
269 }
270
271 void SalomeContainerToolsBase::SetContainerNameOf(Engines::ContainerParameters& params, const std::string& name)
272 {
273   params.container_name=CORBA::string_dup(name.c_str());
274 }
275
276 std::map<std::string,std::string> SalomeContainerTools::getResourceProperties(const std::string& name) const
277 {
278   std::map<std::string,std::string> properties;
279
280   YACS::ENGINE::RuntimeSALOME* runTime = YACS::ENGINE::getSALOMERuntime();
281   CORBA::ORB_ptr orb = runTime->getOrb();
282   if (!orb) return properties;
283   SALOME_NamingService namingService(orb);
284   SALOME_LifeCycleCORBA lcc(&namingService);
285   CORBA::Object_var obj = namingService.Resolve(SALOME_ResourcesManager::_ResourcesManagerNameInNS);
286   if (CORBA::is_nil(obj))
287     return properties;
288   Engines::ResourcesManager_var resManager = Engines::ResourcesManager::_narrow(obj);
289   if (CORBA::is_nil(resManager))
290     return properties;
291
292   std::ostringstream value;
293   Engines::ResourceDefinition_var resource_definition = resManager->GetResourceDefinition(name.c_str());
294   properties["hostname"]=resource_definition->hostname.in();
295   properties["OS"]=resource_definition->OS.in();
296   value.str(""); value << resource_definition->mem_mb;
297   properties["mem_mb"]=value.str();
298   value.str(""); value << resource_definition->cpu_clock;
299   properties["cpu_clock"]=value.str();
300   value.str(""); value << resource_definition->nb_node;
301   properties["nb_node"]=value.str();
302   value.str(""); value << resource_definition->nb_proc_per_node;
303   properties["nb_proc_per_node"]=value.str();
304   /*
305   properties["component_list"]="";
306   for(CORBA::ULong i=0; i < resource_definition->componentList.length(); i++)
307     {
308       if(i > 0)
309         properties["component_list"]=properties["component_list"]+",";
310       properties["component_list"]=properties["component_list"]+resource_definition->componentList[i].in();
311     }
312     */
313   return properties;
314 }
315
316 /*!
317  * \param [in] compoNames
318  * \param [in,out] shutdownLevel
319  */
320 void SalomeContainerToolsBase::Start(const std::vector<std::string>& compoNames, SalomeContainerHelper *schelp, SalomeContainerToolsBase& sct, int& shutdownLevel, const Container *cont, const Task *askingNode)
321 {
322   CORBA::ORB_ptr orb(getSALOMERuntime()->getOrb());
323   SALOME_NamingService ns;
324   try
325   {
326       ns.init_orb(orb);
327   }
328   catch(SALOME_Exception& e)
329   {
330       throw Exception("SalomeContainer::start : Unable to contact the SALOME Naming Service");
331   }
332   CORBA::Object_var obj(ns.Resolve(SALOME_ContainerManager::_ContainerManagerNameInNS));
333   Engines::ContainerManager_var contManager(Engines::ContainerManager::_narrow(obj));
334
335   bool isEmptyName;
336   std::string str(sct.getNotNullContainerName(cont,askingNode,isEmptyName));
337   DEBTRACE("SalomeContainer::start " << str <<";"<< sct.getHostName() );
338
339   // Finalize parameters with components found in the container
340
341   for(std::vector<std::string>::const_iterator iter=compoNames.begin();iter!=compoNames.end();iter++)
342     sct.addToComponentList(*iter);
343
344   Engines::ContainerParameters myparams(sct.getParameters());
345   {
346     std::string dftLauchMode(schelp->getDftLaunchMode());
347     myparams.mode=CORBA::string_dup(dftLauchMode.c_str());
348   }
349
350   //If a container_name is given try to find an already existing container in naming service
351   //If not found start a new container with the given parameters
352   if (dynamic_cast<SalomeContainerMonoHelper *>(schelp) && !isEmptyName)
353     {
354       myparams.mode=CORBA::string_dup("getorstart");
355     }
356
357   if (isEmptyName)
358     {
359       shutdownLevel=1;
360     }
361   //sct.setContainerName(str);
362   SetContainerNameOf(myparams,str);
363   Engines::Container_var trueCont(Engines::Container::_nil());
364   if(!isEmptyName && shutdownLevel==999)
365     {
366       //Make this only the first time start is called (_shutdownLevel==999)
367       //If the container is named, first try to get an existing container
368       //If there is an existing container use it and set the shutdown level to 3
369       //If there is no existing container, try to launch a new one and set the shutdown level to 2
370       myparams.mode="get";
371       try
372       {
373           DEBTRACE("GiveContainer " << str << " mode " << myparams.mode);
374           trueCont=contManager->GiveContainer(myparams);
375       }
376       catch( const SALOME::SALOME_Exception& ex )
377       {
378           std::string msg="SalomeContainer::start : no existing container : ";
379           msg += '\n';
380           msg += ex.details.text.in();
381           DEBTRACE( msg );
382       }
383       catch(...)
384       {
385       }
386
387       if(!CORBA::is_nil(trueCont))
388         {
389           shutdownLevel=3;
390           DEBTRACE( "container found: " << str << " " << shutdownLevel );
391         }
392       else
393         {
394           shutdownLevel=2;
395           myparams.mode="start";
396           DEBTRACE( "container not found: " << str << " " << shutdownLevel);
397         }
398     }
399
400   int nbTries=0;
401   while(CORBA::is_nil(trueCont))
402   {
403     try
404     {
405           // --- GiveContainer is used in batch mode to retreive launched containers,
406           //     and is equivalent to StartContainer when not in batch.
407           DEBTRACE("GiveContainer " << str << " mode " << myparams.mode);
408           trueCont=contManager->GiveContainer(myparams);
409     }
410     catch( const SALOME::SALOME_Exception& ex )
411     {
412         std::string msg="SalomeContainer::start : Unable to launch container in Salome : ";
413         msg += '\n';
414         msg += ex.details.text.in();
415         throw Exception(msg);
416     }
417     catch(CORBA::COMM_FAILURE&)
418     {
419       //std::cerr << "SalomeContainer::start : CORBA Comm failure detected. Make another try!" << std::endl;
420       DEBTRACE("SalomeContainer::start :" << str << " :CORBA Comm failure detected. Make another try!");
421       nbTries++;
422       if(nbTries > 5)
423         throw Exception("SalomeContainer::start : Unable to launch container in Salome : CORBA Comm failure detected");
424     }
425     catch(CORBA::Exception&)
426     {
427         throw Exception("SalomeContainer::start : Unable to launch container in Salome : Unexpected CORBA failure detected");
428     }
429   }
430
431   if(CORBA::is_nil(trueCont))
432     throw Exception("SalomeContainer::start : Unable to launch container in Salome. Check your CatalogResources.xml file");
433
434   // TODO : thread safety!
435   schelp->setContainer(askingNode,trueCont);
436
437   CORBA::String_var containerName(trueCont->name()),hostName(trueCont->getHostName());
438   //std::cerr << "SalomeContainer launched : " << containerName << " " << hostName << " " << trueCont->getPID() << std::endl;
439   DEBTRACE("SalomeContainer launched : " << containerName << " " << hostName << " " << trueCont->getPID() );
440 }
441
442 CORBA::Object_ptr SalomeContainerToolsBase::LoadComponent(SalomeContainerHelper *launchModeType, Container *cont, Task *askingNode)
443 {
444   DEBTRACE("SalomeContainer::loadComponent ");
445   const ComponentInstance *inst(askingNode?askingNode->getComponent():0);
446   {
447     YACS::BASES::AutoLocker<Container> alck(cont);//To be sure
448     if(!cont->isAlreadyStarted(askingNode))
449       cont->start(askingNode);
450   }
451   if(!inst)
452     throw Exception("SalomeContainerToolsBase::LoadComponent : no instance of component in the task requesting for a load of its component !");
453   CORBA::Object_ptr objComponent=CORBA::Object::_nil();
454   {
455     YACS::BASES::AutoLocker<Container> alck(cont);//To be sure
456     std::string compoName(inst->getCompoName());
457     Engines::Container_var container(launchModeType->getContainer(askingNode));
458
459     char *reason;
460     bool isLoadable(container->load_component_Library(compoName.c_str(), reason));
461     if(isLoadable)
462       objComponent=CreateComponentInstance(cont,container,inst);
463   }
464   return objComponent;
465 }
466
467 CORBA::Object_ptr SalomeContainerToolsBase::CreateComponentInstance(Container *cont, Engines::Container_ptr contPtr, const ComponentInstance *inst)
468 {
469   if(!inst)
470     throw Exception("SalomeContainerToolsBase::CreateComponentInstance : no instance of component in the task requesting for a load of its component !");
471   char *reason(0);
472   std::string compoName(inst->getCompoName());
473   CORBA::Object_ptr objComponent=CORBA::Object::_nil();
474   Proc* p(cont->getProc());
475   // prepare component instance properties
476   Engines::FieldsDict_var env(new Engines::FieldsDict);
477   std::map<std::string, std::string> properties(inst->getProperties());
478   if(p)
479     {
480       std::map<std::string,std::string> procMap=p->getProperties();
481       properties.insert(procMap.begin(),procMap.end());
482     }
483
484   std::map<std::string, std::string>::const_iterator itm;
485   env->length(properties.size());
486   int item=0;
487   for(itm = properties.begin(); itm != properties.end(); ++itm, item++)
488     {
489       DEBTRACE("envname="<<itm->first<<" envvalue="<< itm->second);
490       env[item].key= CORBA::string_dup(itm->first.c_str());
491       env[item].value <<= itm->second.c_str();
492     }
493
494   objComponent=contPtr->create_component_instance_env(compoName.c_str(), env, reason);
495   if(CORBA::is_nil(objComponent))
496     {
497       std::string text="Error while trying to create a new component: component '"+ compoName;
498       text=text+"' is not installed or it's a wrong name";
499       text += '\n';
500       text += reason;
501       CORBA::string_free(reason);
502       throw Exception(text);
503     }
504   return objComponent;
505 }
506
507 std::string SalomeContainerToolsBase::GetPlacementId(const SalomeContainerHelper *launchModeType, const Container *cont, const Task *askingNode)
508 {
509   if(cont->isAlreadyStarted(askingNode))
510     {
511       Engines::Container_var container(launchModeType->getContainer(askingNode));
512       const char *what="/";
513       CORBA::String_var corbaStr(container->name());
514       std::string ret(corbaStr);
515
516       //Salome FOREVER ...
517       std::string::size_type i=ret.find_first_of(what,0);
518       i=ret.find_first_of(what, i==std::string::npos ? i:i+1);
519       if(i!=std::string::npos)
520         return ret.substr(i+1);
521       return ret;
522     }
523   else
524     return "Not placed yet !!!";
525 }
526
527 std::string SalomeContainerToolsBase::GetFullPlacementId(const SalomeContainerHelper *launchModeType, const Container *cont, const Task *askingNode)
528 {
529   if(cont->isAlreadyStarted(askingNode))
530     {
531       Engines::Container_var container(launchModeType->getContainer(askingNode));
532       try
533       {
534           CORBA::String_var corbaStr(container->name());
535           std::string ret(corbaStr);
536           return ret;
537       }
538       catch(...)
539       {
540           return "Unknown_placement";
541       }
542     }
543   else
544     return "Not_placed_yet";
545 }
546
547 Engines::ContainerParameters SalomeContainerToolsDecorator::getParameters() const
548 {
549   Engines::ContainerParameters ret(_decorated->getParameters());
550   std::string st(ret.resource_params.hostname);
551   if(!st.empty())
552     return ret;
553   int nbProcPerNode(this->_nb_cores_per_worker);
554   std::size_t iPos(_vh->locateTask(_node)),nPos(_vh->size());
555   if(_vh->size()!=_pg->getNumberOfWorkers(nbProcPerNode))
556     throw YACS::Exception("SalomeContainerToolsDecorator::getParameters : Internal error !");
557   std::string zeMachine(_pg->deduceMachineFrom(iPos,nbProcPerNode));
558   ret.resource_params.hostname=CORBA::string_dup(zeMachine.c_str());
559   return ret;
560 }