Salome HOME
Porting KERNEL on new XML reader.
[modules/kernel.git] / src / Container / SALOME_ContainerManager.cxx
1 // Copyright (C) 2005  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
2 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
3 // 
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.
8 // 
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.
13 //
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
17 //
18 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 //
20 #include "SALOME_ContainerManager.hxx"
21 #include "SALOME_NamingService.hxx"
22 #include "OpUtil.hxx"
23 #include <sys/types.h>
24 #ifndef WNT
25 #include <unistd.h>
26 #endif
27 #include <vector>
28 #include "Utils_CorbaException.hxx"
29
30 #ifdef WITH_PACO_PARALLEL
31 #include "PaCO++.h"
32 #endif
33
34 #define TIME_OUT_TO_LAUNCH_CONT 21
35
36 using namespace std;
37
38 const char *SALOME_ContainerManager::_ContainerManagerNameInNS = 
39   "/ContainerManager";
40
41 //=============================================================================
42 /*! 
43  *  Constructor
44  *  \param orb
45  *  Define a CORBA single thread policy for the server, which avoid to deal
46  *  with non thread-safe usage like Change_Directory in SALOME naming service
47  */
48 //=============================================================================
49
50 SALOME_ContainerManager::SALOME_ContainerManager(CORBA::ORB_ptr orb)
51 {
52   MESSAGE("constructor");
53   _NS = new SALOME_NamingService(orb);
54   _ResManager = new SALOME_ResourcesManager(orb);
55   _id=0;
56   PortableServer::POA_var root_poa = PortableServer::POA::_the_root_poa();
57   PortableServer::POAManager_var pman = root_poa->the_POAManager();
58   PortableServer::POA_var my_poa;
59
60   CORBA::PolicyList policies;
61   policies.length(1);
62   PortableServer::ThreadPolicy_var threadPol = 
63     root_poa->create_thread_policy(PortableServer::SINGLE_THREAD_MODEL);
64   policies[0] = PortableServer::ThreadPolicy::_duplicate(threadPol);
65
66   my_poa = 
67     root_poa->create_POA("SThreadPOA",pman,policies);
68   threadPol->destroy();
69   PortableServer::ObjectId_var id = my_poa->activate_object(this);
70   CORBA::Object_var obj = my_poa->id_to_reference(id);
71   Engines::ContainerManager_var refContMan =
72     Engines::ContainerManager::_narrow(obj);
73
74   _NS->Register(refContMan,_ContainerManagerNameInNS);
75   MESSAGE("constructor end");
76 }
77
78 //=============================================================================
79 /*! 
80  * destructor
81  */
82 //=============================================================================
83
84 SALOME_ContainerManager::~SALOME_ContainerManager()
85 {
86   MESSAGE("destructor");
87   delete _NS;
88   delete _ResManager;
89 }
90
91 //=============================================================================
92 /*! CORBA method:
93  *  shutdown all the containers, then the ContainerManager servant
94  */
95 //=============================================================================
96
97 void SALOME_ContainerManager::Shutdown()
98 {
99   MESSAGE("Shutdown");
100   ShutdownContainers();
101   PortableServer::ObjectId_var oid = _default_POA()->servant_to_id(this);
102   _default_POA()->deactivate_object(oid);
103   _remove_ref();
104   
105 }
106
107 //=============================================================================
108 /*! CORBA Method:
109  *  Loop on all the containers listed in naming service, ask shutdown on each
110  */
111 //=============================================================================
112
113 void SALOME_ContainerManager::ShutdownContainers()
114 {
115   MESSAGE("ShutdownContainers");
116   _NS->Change_Directory("/Containers");
117   vector<string> vec = _NS->list_directory_recurs();
118   list<string> lstCont;
119   for(vector<string>::iterator iter = vec.begin();iter!=vec.end();iter++)
120     {
121       SCRUTE((*iter));
122       CORBA::Object_var obj=_NS->Resolve((*iter).c_str());
123       Engines::Container_var cont=Engines::Container::_narrow(obj);
124       if(!CORBA::is_nil(cont))
125         {
126           lstCont.push_back((*iter));
127         }
128     }
129   MESSAGE("Container list: ");
130   for(list<string>::iterator iter=lstCont.begin();iter!=lstCont.end();iter++)
131     {
132       SCRUTE((*iter));
133     }
134   for(list<string>::iterator iter=lstCont.begin();iter!=lstCont.end();iter++)
135     {
136       SCRUTE((*iter));
137       CORBA::Object_var obj=_NS->Resolve((*iter).c_str());
138       Engines::Container_var cont=Engines::Container::_narrow(obj);
139       if(!CORBA::is_nil(cont))
140         {
141           MESSAGE("ShutdownContainers: " << (*iter));
142           cont->Shutdown();
143         }
144       else MESSAGE("ShutdownContainers: no container ref for " << (*iter));
145     }
146 }
147
148 //=============================================================================
149 /*! CORBA Method:
150  *  Find a suitable Container in a list of machines, or start one
151  *  \param params            Machine Parameters required for the container
152  *  \param possibleComputers list of machines usable for find or start
153  */
154 //=============================================================================
155
156 Engines::Container_ptr
157 SALOME_ContainerManager::
158 FindOrStartContainer(const Engines::MachineParameters& params,
159                      const Engines::MachineList& possibleComputers)
160 {
161   long id;
162   string containerNameInNS;
163   char idc[3*sizeof(long)];
164
165   Engines::Container_ptr ret = FindContainer(params,possibleComputers);
166   if(!CORBA::is_nil(ret))
167     return ret;
168   MESSAGE("Container doesn't exist try to launch it ...");
169
170   return StartContainer(params,possibleComputers,Engines::P_FIRST);
171
172 }
173
174 //=============================================================================
175 /*! CORBA Method:
176  *  Start a suitable Container in a list of machines
177  *  \param params            Machine Parameters required for the container
178  *  \param possibleComputers list of machines usable for start
179  */
180 //=============================================================================
181
182 Engines::Container_ptr
183 SALOME_ContainerManager::
184 StartContainer(const Engines::MachineParameters& params,
185                const Engines::MachineList& possibleComputers,
186                Engines::ResPolicy policy)
187 {
188 #ifdef WITH_PACO_PARALLEL
189   std::string parallelLib(params.parallelLib);
190   if (parallelLib != "")
191     return FindOrStartParallelContainer(params, possibleComputers);
192 #endif
193   long id;
194   string containerNameInNS;
195   char idc[3*sizeof(long)];
196   Engines::Container_ptr ret = Engines::Container::_nil();
197
198   MESSAGE("SALOME_ContainerManager::StartContainer " <<
199           possibleComputers.length());
200
201   string theMachine;
202   try{
203     switch(policy){
204     case Engines::P_FIRST:
205       theMachine=_ResManager->FindFirst(possibleComputers);
206       break;
207     case Engines::P_CYCL:
208       theMachine=_ResManager->FindNext(possibleComputers);
209       break;
210     case Engines::P_BEST:
211       theMachine=_ResManager->FindBest(possibleComputers);
212       break;
213     }
214   }
215   catch( const SALOME_Exception &ex ){
216     MESSAGE(ex.what());
217     return Engines::Container::_nil();
218   }
219
220   MESSAGE("try to launch it on " << theMachine);
221
222   // Get Id for container: a parallel container registers in Naming Service
223   // on the machine where is process 0. ContainerManager does'nt know the name
224   // of this machine before the launch of the parallel container. So to get
225   // the IOR of the parallel container in Naming Service, ContainerManager
226   // gives a unique Id. The parallel container registers his name under
227   // /ContainerManager/Id directory in NamingService
228
229   id = GetIdForContainer();
230
231   string command;
232   if(theMachine==""){
233     MESSAGE("SALOME_ContainerManager::StartContainer : " <<
234             "no possible computer");
235     return Engines::Container::_nil();
236   }
237   else if(theMachine==GetHostname())
238     command=_ResManager->BuildCommandToLaunchLocalContainer(params,id);
239   else
240     command = _ResManager->BuildCommandToLaunchRemoteContainer(theMachine,params,id);
241
242   _ResManager->RmTmpFile();
243   int status=system(command.c_str());
244   if (status == -1){
245     MESSAGE("SALOME_LifeCycleCORBA::StartOrFindContainer rsh failed " <<
246             "(system command status -1)");
247     return Engines::Container::_nil();
248   }
249   else if (status == 217){
250     MESSAGE("SALOME_LifeCycleCORBA::StartOrFindContainer rsh failed " <<
251             "(system command status 217)");
252     return Engines::Container::_nil();
253   }
254   else{
255     int count=TIME_OUT_TO_LAUNCH_CONT;
256     MESSAGE("count = "<<count);
257     while ( CORBA::is_nil(ret) && count ){
258 #ifndef WNT
259       sleep( 1 ) ;
260 #else
261       Sleep(1000);
262 #endif
263       count-- ;
264       if ( count != 10 )
265         MESSAGE( count << ". Waiting for container on " << theMachine);
266
267       if(params.isMPI){
268         containerNameInNS = "/ContainerManager/id";
269         sprintf(idc,"%ld",id);
270         containerNameInNS += idc;
271       }
272       else
273         containerNameInNS = _NS->BuildContainerNameForNS(params,theMachine.c_str());
274
275       SCRUTE(containerNameInNS);
276       CORBA::Object_var obj = _NS->Resolve(containerNameInNS.c_str());
277       ret=Engines::Container::_narrow(obj);
278     }
279     
280     if ( CORBA::is_nil(ret) )
281       MESSAGE("SALOME_LifeCycleCORBA::StartOrFindContainer rsh failed");
282
283     return ret;
284   }
285 }
286
287 //=============================================================================
288 /*! CORBA Method:
289  *  Start a suitable Container in a list of machines
290  *  \param params            Machine Parameters required for the container
291  *  \param possibleComputers list of machines usable for start
292  */
293 //=============================================================================
294
295 Engines::Container_ptr
296 SALOME_ContainerManager::
297 StartContainer(const Engines::MachineParameters& params,
298                Engines::ResPolicy policy)
299 {
300   Engines::MachineList_var possibleComputers = GetFittingResources(params,"");
301   return StartContainer(params,possibleComputers,policy);
302 }
303
304 #ifdef WITH_PACO_PARALLEL
305 //=============================================================================
306 /*! CORBA Method:
307  *  Find or Start a suitable PaCO++ Parallel Container in a list of machines.
308  *  \param params            Machine Parameters required for the container
309  *  \param possibleComputers list of machines usable for find or start
310  *
311  *  \return CORBA container reference.
312  */
313 //=============================================================================
314 Engines::Container_ptr
315 SALOME_ContainerManager::
316 FindOrStartParallelContainer(const Engines::MachineParameters& params_const,
317                              const Engines::MachineList& possibleComputers)
318 {
319   CORBA::Object_var obj;
320   Engines::Container_ptr ret = Engines::Container::_nil();
321   Engines::MachineParameters params(params_const);
322
323   // Step 1 : Try to find a suitable container
324   // Currently not as good as could be since
325   // we have to verified the number of nodes of the container
326   // if a user tell that.
327   ret = FindContainer(params, possibleComputers);
328
329   if(CORBA::is_nil(ret)) {
330     // Step 2 : Starting a new parallel container
331     INFOS("[FindOrStartParallelContainer] Starting a parallel container");
332     
333     // Step 2.1 : Choose a computer
334     string theMachine = _ResManager->FindFirst(possibleComputers);
335     if(theMachine == "") {
336       INFOS("[FindOrStartParallelContainer] !!!!!!!!!!!!!!!!!!!!!!!!!!");
337       INFOS("[FindOrStartParallelContainer] No possible computer found");
338       INFOS("[FindOrStartParallelContainer] !!!!!!!!!!!!!!!!!!!!!!!!!!");
339     }
340     else {
341       INFOS("[FindOrStartParallelContainer] on machine : " << theMachine);
342       string command;
343       if(theMachine == GetHostname()) {
344         // Step 3 : starting parallel container proxy
345         params.hostname = CORBA::string_dup(theMachine.c_str());
346         Engines::MachineParameters params_proxy(params);
347         command = _ResManager->BuildCommandToLaunchLocalParallelContainer("SALOME_ParallelContainerProxy", params_proxy, "xterm");
348         // LaunchParallelContainer uses this value to know if it launches the proxy or the nodes
349         params_proxy.nb_component_nodes = 0;
350         obj = LaunchParallelContainer(command, params_proxy, _NS->ContainerName(params));
351         ret = Engines::Container::_narrow(obj);
352
353         // Step 4 : starting parallel container nodes
354         command = _ResManager->BuildCommandToLaunchLocalParallelContainer("SALOME_ParallelContainerNode", params, "xterm");
355         string name = _NS->ContainerName(params) + "Node";
356         LaunchParallelContainer(command, params, name);
357
358         // Step 5 : connecting nodes and the proxy to actually create a parallel container
359         try {
360         for (int i = 0; i < params.nb_component_nodes; i++) {
361
362         char buffer [5];
363 #ifndef WNT
364           snprintf(buffer,5,"%d",i);
365 #else
366           _snprintf(buffer,5,"%d",i);
367 #endif
368         string name_cont = name + string(buffer);
369
370         string theNodeMachine(CORBA::string_dup(params.hostname));
371         string containerNameInNS = _NS->BuildContainerNameForNS(name_cont.c_str(),theNodeMachine.c_str());
372         int count = TIME_OUT_TO_LAUNCH_CONT;
373         obj = _NS->Resolve(containerNameInNS.c_str());
374         while (CORBA::is_nil(obj) && count) {
375           INFOS("[FindOrStartParallelContainer] CONNECTION FAILED !!!!!!!!!!!!!!!!!!!!!!!!");
376 #ifndef WNT
377           sleep(1) ;
378 #else
379           Sleep(1000);
380 #endif
381           count-- ;
382           obj = _NS->Resolve(containerNameInNS.c_str());
383         }
384
385         PaCO::InterfaceParallel_var node = PaCO::InterfaceParallel::_narrow(obj);
386         MESSAGE("[FindOrStartParallelContainer] Deploying node : " << name);
387         node->deploy(i);
388         }
389         }
390         catch(CORBA::SystemException& e)
391         {
392           INFOS("Caught CORBA::SystemException. : " << e);
393         }
394         catch(PortableServer::POA::ServantAlreadyActive&)
395         {
396           INFOS("Caught CORBA::ServantAlreadyActiveException");
397         }
398         catch(CORBA::Exception&)
399         {
400           INFOS("Caught CORBA::Exception.");
401         }
402         catch(std::exception& exc)
403         {
404           INFOS("Caught std::exception - "<<exc.what()); 
405         }
406         catch(...)
407         {
408           INFOS("Caught unknown exception.");
409         }
410         INFOS("[FindOrStartParallelContainer] node " << name << " deployed");
411       }
412
413       else {
414         INFOS("[FindOrStartParallelContainer] Currently parallel containers are launched only on the local host");
415       }
416     }
417   }
418   return ret;
419 }
420 #else
421 //=============================================================================
422 /*! CORBA Method:
423  *  Find or Start a suitable PaCO++ Parallel Container in a list of machines.
424  *  \param params            Machine Parameters required for the container
425  *  \param possibleComputers list of machines usable for find or start
426  *
427  *  \return CORBA container reference.
428  */
429 //=============================================================================
430 Engines::Container_ptr
431 SALOME_ContainerManager::
432 FindOrStartParallelContainer(const Engines::MachineParameters& params,
433                              const Engines::MachineList& possibleComputers)
434 {
435   Engines::Container_ptr ret = Engines::Container::_nil();
436   INFOS("[FindOrStartParallelContainer] is disabled !");
437   INFOS("[FindOrStartParallelContainer] recompile SALOME Kernel to enable parallel extension");
438   return ret;
439 }
440 #endif
441
442 //=============================================================================
443 /*! 
444  * 
445  */
446 //=============================================================================
447
448 Engines::MachineList *
449 SALOME_ContainerManager::
450 GetFittingResources(const Engines::MachineParameters& params,
451                     const char *componentName)
452 {
453   MESSAGE("SALOME_ContainerManager::GetFittingResources");
454   Engines::MachineList *ret=new Engines::MachineList;
455   vector<string> vec;
456   try
457     {
458       vec = _ResManager->GetFittingResources(params,componentName);
459     }
460   catch(const SALOME_Exception &ex)
461     {
462       INFOS("Caught exception.");
463       THROW_SALOME_CORBA_EXCEPTION(ex.what(),SALOME::BAD_PARAM);
464       //return ret;
465     }
466
467   //  MESSAGE("Machine list length "<<vec.size());
468   ret->length(vec.size());
469   for(unsigned int i=0;i<vec.size();i++)
470     {
471       (*ret)[i]=(vec[i]).c_str();
472     }
473   return ret;
474 }
475
476 //=============================================================================
477 /*! 
478  * 
479  */
480 //=============================================================================
481
482 char*
483 SALOME_ContainerManager::
484 FindFirst(const Engines::MachineList& possibleComputers)
485 {
486   string theMachine=_ResManager->FindFirst(possibleComputers);
487   return CORBA::string_dup(theMachine.c_str());
488 }
489
490 //=============================================================================
491 /*! 
492  * 
493  */
494 //=============================================================================
495
496 Engines::Container_ptr
497 SALOME_ContainerManager::
498 FindContainer(const Engines::MachineParameters& params,
499               const char *theMachine)
500 {
501   string containerNameInNS(_NS->BuildContainerNameForNS(params,theMachine));
502   CORBA::Object_var obj = _NS->Resolve(containerNameInNS.c_str());
503   if( !CORBA::is_nil(obj) )
504     return Engines::Container::_narrow(obj);
505   else
506     return Engines::Container::_nil();
507 }
508
509 //=============================================================================
510 /*! 
511  * 
512  */
513 //=============================================================================
514
515 Engines::Container_ptr
516 SALOME_ContainerManager::
517 FindContainer(const Engines::MachineParameters& params,
518               const Engines::MachineList& possibleComputers)
519 {
520   MESSAGE("FindContainer "<<possibleComputers.length());
521   for(unsigned int i=0;i<possibleComputers.length();i++)
522     {
523       MESSAGE("FindContainer possible " << possibleComputers[i]);
524       Engines::Container_ptr cont = FindContainer(params,possibleComputers[i]);
525       if( !CORBA::is_nil(cont) )
526         return cont;
527     }
528   MESSAGE("FindContainer: not found");
529   return Engines::Container::_nil();
530 }
531
532 //=============================================================================
533 /*! This method launches the parallel container.
534  *  It will may be placed on the ressources manager.
535  *
536  * \param command to launch
537  * \param container's parameters
538  * \param name of the container
539  *
540  * \return CORBA container reference
541  */
542 //=============================================================================
543 CORBA::Object_ptr 
544 SALOME_ContainerManager::LaunchParallelContainer(const std::string& command, 
545                                                  const Engines::MachineParameters& params,
546                                                  const std::string& name)
547 {
548   CORBA::Object_ptr obj = CORBA::Object::_nil();
549   string containerNameInNS;
550
551   if (params.nb_component_nodes == 0) {
552     INFOS("[LaunchParallelContainer] launching the proxy of the parallel container");
553     int status = system(command.c_str());
554     if (status == -1) {
555       INFOS("[LaunchParallelContainer] failed : system command status -1");
556     }
557     else if (status == 217) {
558       INFOS("[LaunchParallelContainer] failed : system command status 217");
559     }
560
561     int count = TIME_OUT_TO_LAUNCH_CONT;
562     string theMachine(CORBA::string_dup(params.hostname));
563     containerNameInNS = _NS->BuildContainerNameForNS((char*) name.c_str(),theMachine.c_str());
564
565     INFOS("[LaunchContainer]  Waiting for Parallel Container proxy on " << theMachine);
566     while (CORBA::is_nil(obj) && count) {
567 #ifndef WNT
568       sleep(1) ;
569 #else
570       Sleep(1000);
571 #endif
572       count-- ;
573       obj = _NS->Resolve(containerNameInNS.c_str());
574     }
575   }
576   else {
577     INFOS("[LaunchParallelContainer] launching the nodes of the parallel container");
578     int status = system(command.c_str());
579     if (status == -1) {
580       INFOS("[LaunchParallelContainer] failed : system command status -1");
581     }
582     else if (status == 217) {
583       INFOS("[LaunchParallelContainer] failed : system command status 217");
584     }
585     // We are waiting all the nodes
586     for (int i = 0; i < params.nb_component_nodes; i++) {
587       obj = CORBA::Object::_nil();
588       int count = TIME_OUT_TO_LAUNCH_CONT;
589
590       // Name of the node
591       char buffer [5];
592 #ifndef WNT
593       snprintf(buffer,5,"%d",i);
594 #else
595       _snprintf(buffer,5,"%d",i);
596 #endif
597
598       string name_cont = name + string(buffer);
599
600       // I don't like this...
601       string theMachine(CORBA::string_dup(params.hostname));
602       containerNameInNS = _NS->BuildContainerNameForNS((char*) name_cont.c_str(),theMachine.c_str());
603       cerr << "[LaunchContainer]  Waiting for Parllel Container node " << containerNameInNS << " on " << theMachine << endl;
604       while (CORBA::is_nil(obj) && count) {
605 #ifndef WNT
606         sleep(1) ;
607 #else
608         Sleep(1000);
609 #endif
610         count-- ;
611         obj = _NS->Resolve(containerNameInNS.c_str());
612       }
613     }
614   }
615
616   if ( CORBA::is_nil(obj) ) {
617     INFOS("[LaunchParallelContainer] failed");
618   }
619   return obj;
620 }
621
622 //=============================================================================
623 /*! 
624  * Get Id for container: a parallel container registers in Naming Service
625  * on the machine where is process 0. ContainerManager does'nt know the name
626  * of this machine before the launch of the parallel container. So to get
627  * the IOR of the parallel container in Naming Service, ContainerManager
628  * gives a unique Id. The parallel container registers his name under
629  * /ContainerManager/Id directory in NamingService
630  */
631 //=============================================================================
632
633
634 long SALOME_ContainerManager::GetIdForContainer(void)
635 {
636   _id++;
637   return _id;
638 }
639