From fb2b44b105b748c6a9856ec414644127e95d13d8 Mon Sep 17 00:00:00 2001 From: Anthony Geay Date: Tue, 26 Mar 2024 17:50:58 +0100 Subject: [PATCH] [EDF29852] : add a new test of management of SIGKILL in containers --- src/Basics/KernelBasis.i | 1 + src/Container/SALOME_ContainerHelper.py | 7 +- src/Container/SALOME_PyNode.py | 15 ++- src/Launcher/Test/CMakeLists.txt | 2 +- src/Launcher/Test/CTestTestfileInstall.cmake | 4 + src/Launcher/Test/testCrashProofContainer.py | 111 +++++++++++++++++++ 6 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 src/Launcher/Test/testCrashProofContainer.py diff --git a/src/Basics/KernelBasis.i b/src/Basics/KernelBasis.i index 904878e3b..8979e3eab 100644 --- a/src/Basics/KernelBasis.i +++ b/src/Basics/KernelBasis.i @@ -33,6 +33,7 @@ using namespace SALOME; %include std_vector.i %template(dvec) std::vector; +%template(strvec) std::vector; %exception { try diff --git a/src/Container/SALOME_ContainerHelper.py b/src/Container/SALOME_ContainerHelper.py index 70bd9d6f6..82bef6bfa 100644 --- a/src/Container/SALOME_ContainerHelper.py +++ b/src/Container/SALOME_ContainerHelper.py @@ -432,9 +432,14 @@ class ScriptInfoAbstract: class ScriptInfoClt(ScriptInfoAbstract): def __init__(self, scriptPtr): + def unPickledSafe( dataPickled ): + if len(dataPickled) > 0: + return pickle.loads(dataPickled) + else: + return None self._node_name = scriptPtr.getName() self._code = scriptPtr.getCode() - self._exec = [pickle.loads(elt.getObj()) for elt in scriptPtr.listOfExecs()] + self._exec = [unPickledSafe(elt.getObj()) for elt in scriptPtr.listOfExecs()] class ScriptInfo(ScriptInfoAbstract): def __init__(self, nodeName, code, execs): diff --git a/src/Container/SALOME_PyNode.py b/src/Container/SALOME_PyNode.py index 80ad94c75..02cfb9188 100644 --- a/src/Container/SALOME_PyNode.py +++ b/src/Container/SALOME_PyNode.py @@ -767,17 +767,24 @@ class PythonFunctionEvaluatorParams: import os return "To clean files : ( cd {} && rm {} )".format( os.path.dirname(self._main_filename)," ".join( [os.path.basename(self._main_filename),self._code_filename,self._in_context_filename] ) ) - def strDependingOnReturnCode(self, returnCode): + def strDependingOnReturnCode(self, keepFilesToReplay, returnCode): if returnCode == -1: return f"return with non zero code ({returnCode})" else: banner = 200*"*" - return f"""return with non zero code ({returnCode}) + if keepFilesToReplay: + return f"""return with non zero code ({returnCode}) {banner} -Looks like a hard crash as returnCode {returnCode} != 1 +Looks like a hard crash as returnCode {returnCode} != 0 {self.replayCmd} {self.cleanOperations} {banner} +""" + else: + return f"""return with non zero code ({returnCode}) +{banner} +Looks like a hard crash as returnCode {returnCode} != 0 +{banner} """ def ExecCrashProofGeneric( code, context, outargsname, instanceOfLogOfCurrentSession, keepFilesToReplay ): @@ -843,7 +850,7 @@ def ExecCrashProofGeneric( code, context, outargsname, instanceOfLogOfCurrentSes evParams.destroyOnKO() else: evParams.destroyOnOK() - raise RuntimeError(f"Subprocess launched {evParams.strDependingOnReturnCode(returnCode)}stdout :\n{stdout}\nstderr :\n{stderr}") + raise RuntimeError(f"Subprocess launched {evParams.strDependingOnReturnCode(keepFilesToReplay,returnCode)}stdout :\n{stdout}\nstderr :\n{stderr}") def ExecCrashProofWithReplay( code, context, outargsname, instanceOfLogOfCurrentSession ): return ExecCrashProofGeneric(code, context, outargsname, instanceOfLogOfCurrentSession, True) diff --git a/src/Launcher/Test/CMakeLists.txt b/src/Launcher/Test/CMakeLists.txt index b266d0ce7..0271f039b 100644 --- a/src/Launcher/Test/CMakeLists.txt +++ b/src/Launcher/Test/CMakeLists.txt @@ -22,7 +22,7 @@ IF(NOT WIN32) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test_launcher.py ${CMAKE_CURRENT_SOURCE_DIR}/TestSSLAttached.py - ${CMAKE_CURRENT_SOURCE_DIR}/testPerfLogManager1.py DESTINATION ${KERNEL_TEST_DIR}/Launcher) + ${CMAKE_CURRENT_SOURCE_DIR}/testPerfLogManager1.py ${CMAKE_CURRENT_SOURCE_DIR}/testCrashProofContainer.py DESTINATION ${KERNEL_TEST_DIR}/Launcher) INSTALL(FILES CTestTestfileInstall.cmake DESTINATION ${KERNEL_TEST_DIR}/Launcher diff --git a/src/Launcher/Test/CTestTestfileInstall.cmake b/src/Launcher/Test/CTestTestfileInstall.cmake index add423874..69f747159 100644 --- a/src/Launcher/Test/CTestTestfileInstall.cmake +++ b/src/Launcher/Test/CTestTestfileInstall.cmake @@ -33,6 +33,10 @@ IF(NOT WIN32) SET(TEST_NAME ${COMPONENT_NAME}_PerfLogManager1) ADD_TEST(${TEST_NAME} ${PYTHON_TEST_DRIVER} 2000 testPerfLogManager1.py) SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES LABELS "${COMPONENT_NAME}") + + SET(TEST_NAME ${COMPONENT_NAME}_testCrashProofContainer) + ADD_TEST(${TEST_NAME} ${PYTHON_TEST_DRIVER} 2000 testCrashProofContainer.py) + SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES LABELS "${COMPONENT_NAME}") # /!\ DO NOT SET TIMEOUT PROPERTY IF USING ${SALOME_TEST_DRIVER} # BUT PASS TIMEOUT VALUE TO THE DRIVER diff --git a/src/Launcher/Test/testCrashProofContainer.py b/src/Launcher/Test/testCrashProofContainer.py new file mode 100644 index 000000000..cb1720445 --- /dev/null +++ b/src/Launcher/Test/testCrashProofContainer.py @@ -0,0 +1,111 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2024 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +import unittest +import os +import salome +import Engines +import pylauncher +import SALOME_PyNode +import KernelBasis +import SALOME + +import glob +import pickle +import tempfile +import logging +from datetime import datetime + + +killMeCode = """ +import os +j = 7 * i +os.kill( os.getpid() , signal.SIGKILL)# the aim of test is here +""" + +normalCode = """ +j = 8 * i +my_log_4_this_session.addFreestyleAndFlush( ("a",777) ) # to check that hidden var is still accessible +""" + +class testPerfLogManager1(unittest.TestCase): + def tess0(self): + """ + EDF29852 : Kill container with OutOfProcessNoReplay mode and see if container still responds. + """ + salome.salome_init() + assert(isinstance(KernelBasis.GetAllPyExecutionModes(),tuple)) + KernelBasis.SetPyExecutionMode("OutOfProcessNoReplay") # the aim of test is here + hostname = "localhost" + cp = pylauncher.GetRequestForGiveContainer(hostname,"container_crash_test") + salome.cm.SetOverrideEnvForContainersSimple(env = [("SALOME_BIG_OBJ_ON_DISK_THRES","1000")]) + cont = salome.cm.GiveContainer(cp) + poa = salome.orb.resolve_initial_references("RootPOA") + obj = SALOME_PyNode.SenderByte_i(poa,pickle.dumps( (["i"],{"i": 3} ) )) ; id_o = poa.activate_object(obj) ; refPtr = poa.id_to_reference(id_o) + pyscript2 = cont.createPyScriptNode("testScript2",killMeCode) + pyscript2.executeFirst(refPtr) + self.assertRaises(SALOME.SALOME_Exception,pyscript2.executeSecond,["j"]) # an agressive SIGKILL has been received and container is still alive :) - it throws an exception :) + pyscript2.UnRegister() + pyscript3 = cont.createPyScriptNode("testScript3",normalCode) + obj = SALOME_PyNode.SenderByte_i(poa,pickle.dumps( (["i"],{"i": 3} ) )) ; id_o = poa.activate_object(obj) ; refPtr = poa.id_to_reference(id_o) + pyscript3.executeFirst(refPtr) + ret = pyscript3.executeSecond(["j"]) + ret = pickle.loads( SALOME_PyNode.SeqByteReceiver(ret[0]).data() ) + self.assertEqual(ret,24) # container has received a SIGKILL but it kindly continue to respond :) + a = salome.logm.NaiveFetch() + self.assertEqual(a[0][2][0].get().freestyle,[('a',777)]) + cont.Shutdown() + + def test1(self): + """ + EDF29852 : Same than test0 Kill container with OutOfProcessWithReplay mode and see if container still responds. But in addition we test if the python script is runnable ! + """ + salome.salome_init() + assert(isinstance(KernelBasis.GetAllPyExecutionModes(),tuple)) + KernelBasis.SetPyExecutionMode("OutOfProcessWithReplay") # the aim of test is here + hostname = "localhost" + cp = pylauncher.GetRequestForGiveContainer(hostname,"container_crash_test") + salome.cm.SetOverrideEnvForContainersSimple(env = [("SALOME_BIG_OBJ_ON_DISK_THRES","1000")]) + cont = salome.cm.GiveContainer(cp) + poa = salome.orb.resolve_initial_references("RootPOA") + obj = SALOME_PyNode.SenderByte_i(poa,pickle.dumps( (["i"],{"i": 3} ) )) ; id_o = poa.activate_object(obj) ; refPtr = poa.id_to_reference(id_o) + pyscript2 = cont.createPyScriptNode("testScript2",killMeCode) + pyscript2.executeFirst(refPtr) + self.assertRaises(SALOME.SALOME_Exception,pyscript2.executeSecond,["j"]) # an agressive SIGKILL has been received and container is still alive :) - it throws an exception :) + pyscript2.UnRegister() + pyscript3 = cont.createPyScriptNode("testScript3",normalCode) + obj = SALOME_PyNode.SenderByte_i(poa,pickle.dumps( (["i"],{"i": 3} ) )) ; id_o = poa.activate_object(obj) ; refPtr = poa.id_to_reference(id_o) + pyscript3.executeFirst(refPtr) + ret = pyscript3.executeSecond(["j"]) + ret = pickle.loads( SALOME_PyNode.SeqByteReceiver(ret[0]).data() ) + self.assertEqual(ret,24) # container has received a SIGKILL but it kindly continue to respond :) + a = salome.logm.NaiveFetch() + self.assertEqual(a[0][2][0].get().freestyle,[('a',777)]) + cont.Shutdown() + +if __name__ == '__main__': + from salome_utils import positionVerbosityOfLoggerRegardingState,setVerboseLevel,setVerbose + salome.standalone() + salome.salome_init() + setVerbose(True) + setVerboseLevel(logging.DEBUG) + positionVerbosityOfLoggerRegardingState() + unittest.main() + -- 2.39.2