From f0a1223aa498c0af420490652f488f262d76b832 Mon Sep 17 00:00:00 2001 From: Margarita KARPUNINA Date: Thu, 15 Dec 2022 20:27:47 +0300 Subject: [PATCH] [bos #32519][EDF] (2022-T3) Parametrize commands in KERNEL --- src/Container/CMakeLists.txt | 2 + src/Container/SALOME_ContainerManager.cxx | 263 +++++++++++++++++- .../SALOME_CM_LOCAL_MPI_LAN.py | 19 ++ .../SALOME_CM_LOCAL_MPI_MPICH.py | 18 ++ .../SALOME_CM_LOCAL_MPI_OPENMPI.py | 21 ++ .../ScriptsTemplate/SALOME_CM_LOCAL_NO_MPI.py | 25 ++ .../ScriptsTemplate/SALOME_CM_REMOTE.py | 116 ++++++++ .../ScriptsTemplate/script_parameters.py | 38 +++ 8 files changed, 497 insertions(+), 5 deletions(-) create mode 100644 src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_LAN.py create mode 100644 src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_MPICH.py create mode 100644 src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_OPENMPI.py create mode 100644 src/Container/ScriptsTemplate/SALOME_CM_LOCAL_NO_MPI.py create mode 100644 src/Container/ScriptsTemplate/SALOME_CM_REMOTE.py create mode 100644 src/Container/ScriptsTemplate/script_parameters.py diff --git a/src/Container/CMakeLists.txt b/src/Container/CMakeLists.txt index 0c7b8e9b8..0484d29c4 100644 --- a/src/Container/CMakeLists.txt +++ b/src/Container/CMakeLists.txt @@ -145,3 +145,5 @@ SWIG_LINK_LIBRARIES(KernelContainer ${PYTHON_LIBRARIES} ${PLATFORM_LIBS} SalomeC install(TARGETS _KernelContainer DESTINATION ${SALOME_INSTALL_LIBS}) install(FILES ${KernelContainer_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS}) SALOME_INSTALL_SCRIPTS("${_swig_SCRIPTS}" ${SALOME_INSTALL_BINS} EXTRA_DPYS "${SWIG_MODULE_KernelContainer_REAL_NAME}") + +INSTALL(DIRECTORY ScriptsTemplate DESTINATION ${SALOME_KERNEL_INSTALL_RES_DATA}) \ No newline at end of file diff --git a/src/Container/SALOME_ContainerManager.cxx b/src/Container/SALOME_ContainerManager.cxx index b70145d7e..a10833689 100644 --- a/src/Container/SALOME_ContainerManager.cxx +++ b/src/Container/SALOME_ContainerManager.cxx @@ -29,6 +29,7 @@ #include "SALOME_ModuleCatalog.hh" #include "Basics_Utils.hxx" #include "Basics_DirUtils.hxx" +#include "PythonCppUtils.hxx" #include #include #include @@ -39,6 +40,7 @@ #include "Utils_CorbaException.hxx" #include #include +#include #include #include CORBA_CLIENT_HEADER(SALOME_Session) @@ -603,7 +605,7 @@ SALOME_ContainerManager::LaunchContainer(const Engines::ContainerParameters& par logFilename += ".log" ; command += " > " + logFilename + " 2>&1"; MakeTheCommandToBeLaunchedASync(command); - + // launch container with a system call status=SystemThreadSafe(command.c_str()); }//end of critical of section @@ -807,6 +809,112 @@ SALOME_ContainerManager::BuildCommandToLaunchRemoteContainer(const std::string& return command; } +//============================================================================= +//! Return a path to the directory with scripts templates +/*! + * \return the path pointed by SALOME_KERNEL_SCRIPTS_DIR environment variable, if it is defined, + * ${KERNEL_ROOT_DIR}/share/salome/resources/separator/kernel/ScriptsTemplate - otherwise + */ +//============================================================================= +std::string getScriptTemplateFilePath() +{ + auto parseScriptTemplateFilePath = []() -> std::string + { + std::string scriptTemplateFilePath = SALOME_ContainerManager::GetenvThreadSafeAsString("SALOME_KERNEL_SCRIPTS_DIR"); + if (!scriptTemplateFilePath.empty()) + { + return scriptTemplateFilePath; + } + else { + return SALOME_ContainerManager::GetenvThreadSafeAsString("KERNEL_ROOT_DIR") + + "/share/salome/resources/kernel/ScriptsTemplate"; + } + }; + + static const std::string scriptTemplateFilePath = parseScriptTemplateFilePath(); + return scriptTemplateFilePath; +} + +//============================================================================= +//! Return a command line constructed based on Python scripts templates +/*! + * \param theScriptName the name of Python script template + * \param theScriptParameters the queue of parameter values + * \return the command line constructed according to the given parameters + */ +//============================================================================= +std::string GetCommandFromTemplate(const std::string& theScriptName, + std::queue& theScriptParameters) +{ + std::string command; + + PyGILState_STATE gstate = PyGILState_Ensure(); + + PyObject* mod(PyImport_ImportModule(theScriptName.c_str())); + if (!mod) + { + PyObject* sys = PyImport_ImportModule("sys"); + PyObject* sys_path = PyObject_GetAttrString(sys, "path"); + PyObject* folder_path = PyUnicode_FromString(getScriptTemplateFilePath().c_str()); + PyList_Append(sys_path, folder_path); + + mod = PyImport_ImportModule(theScriptName.c_str()); + + Py_XDECREF(folder_path); + Py_XDECREF(sys_path); + Py_XDECREF(sys); + } + + if (mod) + { + PyObject* meth(PyObject_GetAttrString(mod, "command")); + if (!meth) + { + Py_XDECREF(mod); + } + else + { + int id = -1; + PyObject* tuple(PyTuple_New(theScriptParameters.size())); + + auto insert_parameter = [&tuple, &theScriptParameters, &id]() + { + if (!theScriptParameters.empty()) + { + PyTuple_SetItem(tuple, ++id, PyUnicode_FromString(theScriptParameters.front().c_str())); + theScriptParameters.pop(); + } + }; + + while (!theScriptParameters.empty()) + { + insert_parameter(); + } + + PyObject *args(PyTuple_New(1)); + PyTuple_SetItem(args, 0, tuple); + + PyObject *res(PyObject_CallObject(meth, args)); + if (res) + { + command = PyUnicode_AsUTF8(res); + Py_XDECREF(res); + } + + Py_XDECREF(args); + Py_XDECREF(tuple); + Py_XDECREF(meth); + Py_XDECREF(mod); + } + } + + PyGILState_Release(gstate); + + MESSAGE("Command from template is ... " << command << std::endl); + return command; +} +//============================================================================= + //============================================================================= /*! * builds the command to be launched. @@ -814,11 +922,76 @@ SALOME_ContainerManager::BuildCommandToLaunchRemoteContainer(const std::string& //============================================================================= std::string SALOME_ContainerManager::BuildCommandToLaunchLocalContainer(const Engines::ContainerParameters& params, const std::string& machinesFile, const std::string& container_exe, std::string& tmpFileName) const { - tmpFileName = BuildTemporaryFileName(); - std::string command; + // Prepare name of the script to be used + std::string script_name = "SALOME_CM_LOCAL_NO_MPI"; + if (params.isMPI) + { +#ifdef LAM_MPI + script_name = "SALOME_CM_LOCAL_MPI_LAN"; +#elif defined(OPEN_MPI) + script_name = "SALOME_CM_LOCAL_MPI_OPENMPI"; +#elif defined(MPICH) + script_name = "SALOME_CM_LOCAL_MPI_MPICH"; +#endif + } + + // Prepare parameters to use in the Python script: + // 1. All parameters are strings. + // 2. For some booleans use "1" = True, "0" = False. + // 3. If a parameter is NULL, then its value is "NULL". + + std::queue script_parameters; + + // ===== Number of processes (key = "nb_proc") + script_parameters.push(params.isMPI ? std::to_string(params.nb_proc <= 0 ? 1 : params.nb_proc) : "NULL"); + + // ===== Working directory (key = "workdir") and temporary directory flag (key = "isTmpDir") + // A working directory is requested + std::string workdir = params.workingdir.in(); + std::string isTmpDir = std::to_string(0); + if (workdir == "$TEMPDIR") + { + // A new temporary directory is requested + isTmpDir = std::to_string(1); + workdir = Kernel_Utils::GetTmpDir(); + } + script_parameters.push(workdir); + script_parameters.push(isTmpDir); + + // ===== Server name (key = "name_server") + script_parameters.push(Kernel_Utils::GetHostname()); + + // ===== Container (key = "container") + std::string container; + if (params.isMPI) + { + container = isPythonContainer(params.container_name) ? "pyMPI SALOME_ContainerPy.py" : "SALOME_MPIContainer"; + } + else + { + container = isPythonContainer(params.container_name) ? "SALOME_ContainerPy.py" : container_exe; + } + script_parameters.push(container); + + // ===== Container name (key = "container_name") + script_parameters.push(_NS->ContainerName(params)); + + // ===== LIBBATCH node file (key = "libbatch_nodefile") + script_parameters.push(std::to_string(GetenvThreadSafe("LIBBATCH_NODEFILE") != NULL ? 1 : 0)); + + // ===== Machine file (key = "machine_file") + script_parameters.push(machinesFile.empty() ? "NULL" : machinesFile); + + // ===== OMPI uri file (key = "ompi_uri_file") + std::string ompi_uri_file = GetenvThreadSafeAsString("OMPI_URI_FILE"); + script_parameters.push(ompi_uri_file.empty() ? "NULL" : ompi_uri_file); + + std::string command_from_template = GetCommandFromTemplate(script_name, script_parameters); std::ostringstream o; + o << command_from_template << " "; + /* //============================================================================ if (params.isMPI) { int nbproc = params.nb_proc <= 0 ? 1 : params.nb_proc; @@ -887,6 +1060,7 @@ std::string SALOME_ContainerManager::BuildCommandToLaunchLocalContainer(const En } o << _NS->ContainerName(params) << " "; + //==================================================================================== */ if( this->_isSSL ) { @@ -900,6 +1074,7 @@ std::string SALOME_ContainerManager::BuildCommandToLaunchLocalContainer(const En AddOmninamesParams(o); } + tmpFileName = BuildTemporaryFileName(); std::ofstream command_file( tmpFileName.c_str() ); command_file << o.str(); command_file.close(); @@ -907,8 +1082,8 @@ std::string SALOME_ContainerManager::BuildCommandToLaunchLocalContainer(const En #ifndef WIN32 chmod(tmpFileName.c_str(), 0x1ED); #endif - command = tmpFileName; - + + std::string command = tmpFileName; MESSAGE("Command is file ... " << command); MESSAGE("Command is ... " << o.str()); return command; @@ -1255,6 +1430,83 @@ std::string SALOME_ContainerManager::getCommandToRunRemoteProcess(AccessProtocol const std::string & workdir) const { std::ostringstream command; + + // Prepare parameters to use in the Python script: + // 1. All parameters are strings. + // 2. For some booleans use "1" = True, "0" = False. + // 3. If a parameter is NULL, then its value is "NULL". + + std::queue script_parameters; + + // ===== Protocol (key = "protocol") + std::string strProtocol; + switch (protocol) + { + case rsh: strProtocol = "rsh"; break; + case ssh: strProtocol = "ssh"; break; + case srun: strProtocol = "srun"; break; + case pbsdsh: strProtocol = "pbsdsh"; break; + case blaunch: strProtocol = "blaunch"; break; + default: + throw SALOME_Exception("Unknown protocol"); + } + script_parameters.push(strProtocol); + + // ===== User name (key = "user") + script_parameters.push(username.empty() ? "NULL" : username); + + // ===== Host name (key = "host") + script_parameters.push(hostname.empty() ? "NULL" : hostname); + + + // ===== Remote APPLI path (key = "appli") + script_parameters.push(applipath.empty() ? GetenvThreadSafeAsString("APPLI") : applipath); + + if(!this->_isSSL) + { + ASSERT(GetenvThreadSafe("NSHOST")); + ASSERT(GetenvThreadSafe("NSPORT")); + } + + struct stat statbuf; + std::string appli_mode = (stat(GetenvThreadSafe("APPLI"), &statbuf) == 0 && S_ISREG(statbuf.st_mode)) ? "launcher" : "dir"; + + // ===== Working directory (key = "workdir") + script_parameters.push(workdir == "$TEMPDIR" ? "\\$TEMPDIR" : workdir); + + // ===== SSL (key = "ssl") + script_parameters.push(this->_isSSL ? "1" : "0"); + + // ===== Hostname of CORBA name server (key = "nshost") + std::string nshost = GetenvThreadSafeAsString("NSHOST"); + script_parameters.push(nshost.empty() ? "NULL" : nshost); + + // ===== Port of CORBA name server (key = "nsport") + std::string nsport = GetenvThreadSafeAsString("NSPORT"); + script_parameters.push(nsport.empty() ? "NULL" : nsport); + + // ===== Remote script (key = "remote_script") + std::string remoteScript = this->GetRunRemoteExecutableScript(); + script_parameters.push(remoteScript.empty() ? "NONE" : remoteScript); + + // ===== Naming service (key = "naming_service") + std::string namingService = "NONE"; + if(this->_isSSL) + { + Engines::EmbeddedNamingService_var ns = GetEmbeddedNamingService(); + CORBA::String_var iorNS = _orb->object_to_string(ns); + namingService = iorNS; + } + script_parameters.push(namingService); + + // ===== APPLI mode (key = "appli_mode") + // $APPLI points either to an application directory, or to a salome launcher file + // we prepare the remote command according to the case + script_parameters.push(appli_mode); + + command << GetCommandFromTemplate("SALOME_CM_REMOTE", script_parameters); + + /* //==================================================================================== bool envd = true; // source the environment switch (protocol) { @@ -1353,6 +1605,7 @@ std::string SALOME_ContainerManager::getCommandToRunRemoteProcess(AccessProtocol command << "'"; } } + //==================================================================================== */ return command.str(); } diff --git a/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_LAN.py b/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_LAN.py new file mode 100644 index 000000000..d4eb85966 --- /dev/null +++ b/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_LAN.py @@ -0,0 +1,19 @@ +import sys +from script_parameters import ScriptLocalParameters + +def command(args): + options = ScriptLocalParameters(args) + if options.debug: print(options) + + cmd = [] + cmd.append("mpirun -np " + options.nb_proc) + + if options.libbatch_nodefile: + cmd.append("-machinefile %s " % options.machine_file) + + cmd.append("-x PATH,LD_LIBRARY_PATH,OMNIORB_CONFIG,SALOME_trace ") + + cmd.append(options.container) + cmd.append(options.container_name) + + return " ".join(cmd) \ No newline at end of file diff --git a/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_MPICH.py b/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_MPICH.py new file mode 100644 index 000000000..2fd94c74a --- /dev/null +++ b/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_MPICH.py @@ -0,0 +1,18 @@ +import sys +from script_parameters import ScriptLocalParameters + +def command(args): + options = ScriptLocalParameters(args) + if options.debug: print(options) + + cmd = [] + cmd.append("mpirun -np " + options.nb_proc) + + if options.libbatch_nodefile: + cmd.append("-machinefile %s " % options.machine_file) + + cmd.append("-nameserver %s" % options.name_server) + cmd.append(options.container) + cmd.append(options.container_name) + + return " ".join(cmd) \ No newline at end of file diff --git a/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_OPENMPI.py b/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_OPENMPI.py new file mode 100644 index 000000000..a0ff6f833 --- /dev/null +++ b/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_MPI_OPENMPI.py @@ -0,0 +1,21 @@ +import sys +from script_parameters import ScriptLocalParameters + +def command(args): + options = ScriptLocalParameters(args) + if options.debug: print(options) + + cmd = [] + cmd.append("mpirun -np " + options.nb_proc) + + if options.libbatch_nodefile: + cmd.append("-machinefile %s " % options.machine_file) + + cmd.append("-x PATH -x LD_LIBRARY_PATH -x OMNIORB_CONFIG -x SALOME_trace ") + if options.ompi_uri_file: + cmd.append("-ompi-server file:%s" % options.ompi_uri_file) + + cmd.append(options.container) + cmd.append(options.container_name) + + return " ".join(cmd) \ No newline at end of file diff --git a/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_NO_MPI.py b/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_NO_MPI.py new file mode 100644 index 000000000..b291e7356 --- /dev/null +++ b/src/Container/ScriptsTemplate/SALOME_CM_LOCAL_NO_MPI.py @@ -0,0 +1,25 @@ +import sys +from script_parameters import ScriptLocalParameters + +def command(args): + options = ScriptLocalParameters(args) + if options.debug: print(options) + + cmd = [] + if options.workdir: + if options.isTmpDir: + if options.Windows: + cmd.append("cd /d %s\n" % options.workdir) + else: + cmd.append("cd %s;" % options.workdir) + else: + if options.Windows: + cmd.append("mkdir %s" % options.workdir); + cmd.append("cd /d %s\n" % options.workdir) + else: + cmd.append("mkdir -p %s && cd %s;" % (options.workdir, options.workdir)) + + cmd.append(options.container) + cmd.append(options.container_name) + + return " ".join(cmd) \ No newline at end of file diff --git a/src/Container/ScriptsTemplate/SALOME_CM_REMOTE.py b/src/Container/ScriptsTemplate/SALOME_CM_REMOTE.py new file mode 100644 index 000000000..9be7bd72e --- /dev/null +++ b/src/Container/ScriptsTemplate/SALOME_CM_REMOTE.py @@ -0,0 +1,116 @@ +import sys + +class ScriptRemoteParameters: + def __init__(self, args): + self.debug = False + if args[0] == "-d": + self.debug = True + args = args[1:] + + self.protocol = args[0] + self.user = self._read_arg(args[1], "NULL") + self.host = self._read_arg(args[2], "NULL") + self.appli = self._read_arg(args[3], "NULL") + self.workdir = self._read_arg(args[4], "NULL") + self.ssl = True if args[5] == "1" else False + self.nshost = args[6] + self.nsport = args[7] + self.remote_script = self._read_arg(args[8], "NULL") + self.naming_service = self._read_arg(args[9], "NULL") + self.appli_mode = args[10] + + import platform + self.Windows = platform.system() == "Windows" + + def _read_arg(self, value, null_value): + if value == null_value: + return None + return value + + def __str__(self): + str = [] + str.append("protocol: %s" % self.protocol) + str.append("user: %s" % self.user) + str.append("hostname: %s" % self.host) + str.append("appli: %s" % self.appli) + str.append("workdir: %s" % self.workdir) + str.append("ssl: %s" % self.ssl) + str.append("nshost: %s" % self.nshost) + str.append("nsport: %s" % self.nsport) + str.append("remote_script: %s" % self.remote_script) + str.append("naming_service: %s" % self.naming_service) + str.append("appil_mode: %s" % self.appli_mode) + str.append("--") + return "\n".join(str) + +# ---------------------------------------------- +def command(args): + options = ScriptRemoteParameters(args) + if options.debug: print(options) + + # build command depending on protocol + cmd = [] + envd = (options.protocol != "srun") + + if options.protocol == "rsh": + # RSH command + cmd.append("rsh") + if options.user: + cmd.append("-l " + options.user) + cmd.append(options.host) + + elif options.protocol == "ssh": + # SSH command + cmd.append("ssh") + if options.user: + cmd.append("-l " + options.user) + cmd.append(options.host) + + elif options.protocol == "srun": + # srun command + cmd.append("srun") + cmd.append("-n 1 -N 1 -s --mem-per-cpu=0 --cpu-bind=none") + cmd.append("--nodelist=" + options.host) + + elif options.protocol == "pbsdsh": + # pbsdh command + cmd.append("pbsdsh") + cmd.append("-o -h") + cmd.append(options.host) + + elif options.protocol == "blaunch": + # blaunch command + cmd.append("blaunch") + cmd.append("-no-shell") + cmd.append(options.host) + + else: + # unknown protocol + raise ValueError("Unknown protocol: %s" % options.protocol) + + + if options.appli_mode == "dir": + cmd.append(options.appli + "/" + options.remote_script) + if not envd: + cmd.append("--noenvd") + if options.ssl: + cmd.append(options.naming_service) + else: + cmd.append(options.nshost) + cmd.append(options.nsport) + + if options.workdir: + cmd.append(" WORKINGDIR '%s'" % options.workdir) + + elif options.appli_mode == "launcher": + cmd.append(options.appli + " remote") + if not options.ssl: + cmd.append("-m %s -p %s" % (options.nshost, options.nsport)) + + if options.workdir: + cmd.append("-d " + options.workdir) + cmd.append("--") + + # elif ignore other appli_mode value + + return " ".join(cmd) \ No newline at end of file diff --git a/src/Container/ScriptsTemplate/script_parameters.py b/src/Container/ScriptsTemplate/script_parameters.py new file mode 100644 index 000000000..407f24a3e --- /dev/null +++ b/src/Container/ScriptsTemplate/script_parameters.py @@ -0,0 +1,38 @@ +class ScriptLocalParameters: + def __init__(self, args): + self.debug = False + if args[0] == "-d": + self.debug = True + args = args[1:] + + self.nb_proc = self._read_arg(args[0], "NULL") + self.workdir = self._read_arg(args[1], "NULL") + self.isTmpDir = True if args[2] == "1" else False + self.name_server = args[3] + self.container = args[4] + self.container_name = args[5] + self.libbatch_nodefile = self._read_arg(args[6], "NULL") + self.machine_file = self._read_arg(args[7], "NULL") + self.ompi_uri_file = self._read_arg(args[8], "NULL") + + import platform + self.Windows = platform.system() == "Windows" + + def _read_arg(self, value, null_value): + if value == null_value: + return None + return value + + def __str__(self): + str = [] + str.append("nb_proc: %s" % self.nb_proc) + str.append("workdir: %s" % self.workdir) + str.append("isTmpDir: %s" % self.isTmpDir) + str.append("name_server: %s" % self.name_server) + str.append("container: %s" % self.container) + str.append("container_name: %s" % self.container_name) + str.append("libbatch_nodefile: %s" % self.libbatch_nodefile) + str.append("machine_file: %s" % self.machine_file) + str.append("ompi_uri_file: %s" % self.ompi_uri_file) + str.append("--") + return "\n".join(str) \ No newline at end of file -- 2.39.2