def test_shell_command_action_basic(self): models = self.fixtures_loader.load_models( fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']}) action_db = models['actions']['local.yaml'] runner = self._get_runner(action_db, cmd='echo 10') runner.pre_run() status, result, _ = runner.run({}) runner.post_run(status, result) self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertEquals(result['stdout'], 10) # End result should be the same when streaming is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) # Verify initial state output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 0) runner = self._get_runner(action_db, cmd='echo 10') runner.pre_run() status, result, _ = runner.run({}) runner.post_run(status, result) self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertEquals(result['stdout'], 10) output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 1) self.assertEqual(output_dbs[0].output_type, 'stdout') self.assertEqual(output_dbs[0].data, '10\n')
def test_shell_command_action_basic(self): models = self.fixtures_loader.load_models( fixtures_pack="generic", fixtures_dict={"actions": ["local.yaml"]}) action_db = models["actions"]["local.yaml"] runner = self._get_runner(action_db, cmd="echo 10") runner.pre_run() status, result, _ = runner.run({}) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertEqual(result["stdout"], 10) # End result should be the same when streaming is enabled cfg.CONF.set_override(name="stream_output", group="actionrunner", override=True) # Verify initial state output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 0) runner = self._get_runner(action_db, cmd="echo 10") runner.pre_run() status, result, _ = runner.run({}) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertEqual(result["stdout"], 10) output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 1) self.assertEqual(output_dbs[0].output_type, "stdout") self.assertEqual(output_dbs[0].data, "10\n")
def test_real_time_output_streaming_bufsize(self): # Test various values for bufsize and verify it works / doesn't hang the process cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) bufsize_values = [-100, -2, -1, 0, 1, 2, 1024, 2048, 4096, 10000] for index, bufsize in enumerate(bufsize_values, 1): cfg.CONF.set_override(name='stream_output_buffer_size', override=bufsize, group='actionrunner') output_dbs = ActionExecutionOutput.get_all() # Unexpected third party warnings will also inflate this number self.assertGreaterEqual(len(output_dbs), (index - 1) * 4) runner = self._get_mock_runner_obj() runner.entry_point = PRINT_TO_STDOUT_STDERR_ACTION runner.pre_run() (_, output, _) = runner.run({'stdout_count': 2, 'stderr_count': 2}) # assertMultiLineEqual displays a diff if the two don't match self.assertMultiLineEqual(output['stdout'], 'stdout line 0\nstdout line 1\n') # Third party packages can unexpectedly emit warnings and add more # output to the streamed stderr, so we check that the expected # lines occurred, but we allow additional lines to exist self.assertIn('stderr line 0\n', output['stderr']) self.assertIn('stderr line 1\n', output['stderr']) self.assertEqual(output['exit_code'], 0) output_dbs = ActionExecutionOutput.get_all() # Unexpected third party warnings will also inflate this number self.assertGreaterEqual(len(output_dbs), (index) * 4)
def test_real_time_output_streaming_bufsize(self): # Test various values for bufsize and verify it works / doesn't hang the process cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) bufsize_values = [-100, -2, -1, 0, 1, 2, 1024, 2048, 4096, 10000] for index, bufsize in enumerate(bufsize_values, 1): cfg.CONF.set_override(name='stream_output_buffer_size', override=bufsize, group='actionrunner') output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), (index - 1) * 4) runner = self._get_mock_runner_obj() runner.entry_point = PRINT_TO_STDOUT_STDERR_ACTION runner.pre_run() (_, output, _) = runner.run({'stdout_count': 2, 'stderr_count': 2}) self.assertEqual(output['stdout'], 'stdout line 0\nstdout line 1\n') self.assertEqual(output['stderr'], 'stderr line 0\nstderr line 1\n') self.assertEqual(output['exit_code'], 0) output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), (index) * 4)
def test_script_with_paramters_parameter_serialization(self): models = self.fixtures_loader.load_models( fixtures_pack='generic', fixtures_dict={'actions': ['local_script_with_params.yaml']}) action_db = models['actions']['local_script_with_params.yaml'] entry_point = os.path.join( get_fixtures_base_path(), 'generic/actions/local_script_with_params.sh') action_parameters = { 'param_string': 'test string', 'param_integer': 1, 'param_float': 2.55, 'param_boolean': True, 'param_list': ['a', 'b', 'c'], 'param_object': { 'foo': 'bar' } } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertTrue('PARAM_STRING=test string' in result['stdout']) self.assertTrue('PARAM_INTEGER=1' in result['stdout']) self.assertTrue('PARAM_FLOAT=2.55' in result['stdout']) self.assertTrue('PARAM_BOOLEAN=1' in result['stdout']) self.assertTrue('PARAM_LIST=a,b,c' in result['stdout']) self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout']) action_parameters = { 'param_string': 'test string', 'param_integer': 1, 'param_float': 2.55, 'param_boolean': False, 'param_list': ['a', 'b', 'c'], 'param_object': { 'foo': 'bar' } } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertTrue('PARAM_BOOLEAN=0' in result['stdout']) action_parameters = { 'param_string': '', 'param_integer': None, 'param_float': None, } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertTrue('PARAM_STRING=\n' in result['stdout']) self.assertTrue('PARAM_INTEGER=\n' in result['stdout']) self.assertTrue('PARAM_FLOAT=\n' in result['stdout']) # End result should be the same when streaming is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) # Verify initial state output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 0) action_parameters = { 'param_string': 'test string', 'param_integer': 1, 'param_float': 2.55, 'param_boolean': True, 'param_list': ['a', 'b', 'c'], 'param_object': { 'foo': 'bar' } } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertTrue('PARAM_STRING=test string' in result['stdout']) self.assertTrue('PARAM_INTEGER=1' in result['stdout']) self.assertTrue('PARAM_FLOAT=2.55' in result['stdout']) self.assertTrue('PARAM_BOOLEAN=1' in result['stdout']) self.assertTrue('PARAM_LIST=a,b,c' in result['stdout']) self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout']) output_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(output_dbs), 6) self.assertEqual(output_dbs[0].data, 'PARAM_STRING=test string\n') self.assertEqual(output_dbs[5].data, 'PARAM_OBJECT={"foo": "bar"}\n') output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), 0)
def test_action_stdout_and_stderr_is_not_stored_in_db_by_default( self, mock_spawn, mock_popen): # Feature should be disabled by default values = {'delimiter': ACTION_OUTPUT_RESULT_DELIMITER} # Note: We need to mock spawn function so we can test everything in single event loop # iteration mock_spawn.side_effect = blocking_eventlet_spawn # No output to stdout and no result (implicit None) mock_stdout = [ 'pre result line 1\n', '%(delimiter)sTrue%(delimiter)s' % values, 'post result line 1' ] mock_stderr = ['stderr line 1\n', 'stderr line 2\n', 'stderr line 3\n'] mock_process = mock.Mock() mock_process.returncode = 0 mock_popen.return_value = mock_process mock_process.stdout.closed = False mock_process.stderr.closed = False mock_process.stdout.readline = make_mock_stream_readline( mock_process.stdout, mock_stdout, stop_counter=3) mock_process.stderr.readline = make_mock_stream_readline( mock_process.stderr, mock_stderr, stop_counter=3) runner = self._get_mock_runner_obj() runner.entry_point = PASCAL_ROW_ACTION_PATH runner.pre_run() (_, output, _) = runner.run({'row_index': 4}) self.assertEqual(output['stdout'], 'pre result line 1\npost result line 1') self.assertEqual(output['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3\n') self.assertEqual(output['result'], 'True') self.assertEqual(output['exit_code'], 0) output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 0) # False is a default behavior so end result should be the same cfg.CONF.set_override(name='stream_output', group='actionrunner', override=False) mock_process = mock.Mock() mock_process.returncode = 0 mock_popen.return_value = mock_process mock_process.stdout.closed = False mock_process.stderr.closed = False mock_process.stdout.readline = make_mock_stream_readline( mock_process.stdout, mock_stdout, stop_counter=3) mock_process.stderr.readline = make_mock_stream_readline( mock_process.stderr, mock_stderr, stop_counter=3) runner.pre_run() (_, output, _) = runner.run({'row_index': 4}) self.assertEqual(output['stdout'], 'pre result line 1\npost result line 1') self.assertEqual(output['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3\n') self.assertEqual(output['result'], 'True') self.assertEqual(output['exit_code'], 0) output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 0)
def test_script_with_parameters_parameter_serialization(self): models = self.fixtures_loader.load_models( fixtures_pack="generic", fixtures_dict={"actions": ["local_script_with_params.yaml"]}, ) action_db = models["actions"]["local_script_with_params.yaml"] entry_point = os.path.join( get_fixtures_base_path(), "generic/actions/local_script_with_params.sh") action_parameters = { "param_string": "test string", "param_integer": 1, "param_float": 2.55, "param_boolean": True, "param_list": ["a", "b", "c"], "param_object": { "foo": "bar" }, } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertIn("PARAM_STRING=test string", result["stdout"]) self.assertIn("PARAM_INTEGER=1", result["stdout"]) self.assertIn("PARAM_FLOAT=2.55", result["stdout"]) self.assertIn("PARAM_BOOLEAN=1", result["stdout"]) self.assertIn("PARAM_LIST=a,b,c", result["stdout"]) self.assertIn('PARAM_OBJECT={"foo":"bar"}', result["stdout"]) action_parameters = { "param_string": "test string", "param_integer": 1, "param_float": 2.55, "param_boolean": False, "param_list": ["a", "b", "c"], "param_object": { "foo": "bar" }, } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertIn("PARAM_BOOLEAN=0", result["stdout"]) action_parameters = { "param_string": "", "param_integer": None, "param_float": None, } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertIn("PARAM_STRING=\n", result["stdout"]) self.assertIn("PARAM_INTEGER=\n", result["stdout"]) self.assertIn("PARAM_FLOAT=\n", result["stdout"]) # End result should be the same when streaming is enabled cfg.CONF.set_override(name="stream_output", group="actionrunner", override=True) # Verify initial state output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 0) action_parameters = { "param_string": "test string", "param_integer": 1, "param_float": 2.55, "param_boolean": True, "param_list": ["a", "b", "c"], "param_object": { "foo": "bar" }, } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertIn("PARAM_STRING=test string", result["stdout"]) self.assertIn("PARAM_INTEGER=1", result["stdout"]) self.assertIn("PARAM_FLOAT=2.55", result["stdout"]) self.assertIn("PARAM_BOOLEAN=1", result["stdout"]) self.assertIn("PARAM_LIST=a,b,c", result["stdout"]) self.assertIn('PARAM_OBJECT={"foo":"bar"}', result["stdout"]) output_dbs = ActionExecutionOutput.query(output_type="stdout") self.assertEqual(len(output_dbs), 6) self.assertEqual(output_dbs[0].data, "PARAM_STRING=test string\n") self.assertEqual(output_dbs[5].data, 'PARAM_OBJECT={"foo":"bar"}\n') output_dbs = ActionExecutionOutput.query(output_type="stderr") self.assertEqual(len(output_dbs), 0)
def test_script_with_parameters_parameter_serialization(self): models = self.fixtures_loader.load_models( fixtures_pack='generic', fixtures_dict={'actions': ['local_script_with_params.yaml']}) action_db = models['actions']['local_script_with_params.yaml'] entry_point = os.path.join(get_fixtures_base_path(), 'generic/actions/local_script_with_params.sh') action_parameters = { 'param_string': 'test string', 'param_integer': 1, 'param_float': 2.55, 'param_boolean': True, 'param_list': ['a', 'b', 'c'], 'param_object': {'foo': 'bar'} } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertTrue('PARAM_STRING=test string' in result['stdout']) self.assertTrue('PARAM_INTEGER=1' in result['stdout']) self.assertTrue('PARAM_FLOAT=2.55' in result['stdout']) self.assertTrue('PARAM_BOOLEAN=1' in result['stdout']) self.assertTrue('PARAM_LIST=a,b,c' in result['stdout']) self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout']) action_parameters = { 'param_string': 'test string', 'param_integer': 1, 'param_float': 2.55, 'param_boolean': False, 'param_list': ['a', 'b', 'c'], 'param_object': {'foo': 'bar'} } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertTrue('PARAM_BOOLEAN=0' in result['stdout']) action_parameters = { 'param_string': '', 'param_integer': None, 'param_float': None, } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertTrue('PARAM_STRING=\n' in result['stdout']) self.assertTrue('PARAM_INTEGER=\n' in result['stdout']) self.assertTrue('PARAM_FLOAT=\n' in result['stdout']) # End result should be the same when streaming is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) # Verify initial state output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 0) action_parameters = { 'param_string': 'test string', 'param_integer': 1, 'param_float': 2.55, 'param_boolean': True, 'param_list': ['a', 'b', 'c'], 'param_object': {'foo': 'bar'} } runner = self._get_runner(action_db=action_db, entry_point=entry_point) runner.pre_run() status, result, _ = runner.run(action_parameters=action_parameters) runner.post_run(status, result) self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertTrue('PARAM_STRING=test string' in result['stdout']) self.assertTrue('PARAM_INTEGER=1' in result['stdout']) self.assertTrue('PARAM_FLOAT=2.55' in result['stdout']) self.assertTrue('PARAM_BOOLEAN=1' in result['stdout']) self.assertTrue('PARAM_LIST=a,b,c' in result['stdout']) self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout']) output_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(output_dbs), 6) self.assertEqual(output_dbs[0].data, 'PARAM_STRING=test string\n') self.assertEqual(output_dbs[5].data, 'PARAM_OBJECT={"foo": "bar"}\n') output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), 0)
def test_action_stdout_and_stderr_is_not_stored_in_db_by_default(self, mock_spawn, mock_popen): # Feature should be disabled by default values = {'delimiter': ACTION_OUTPUT_RESULT_DELIMITER} # Note: We need to mock spawn function so we can test everything in single event loop # iteration mock_spawn.side_effect = blocking_eventlet_spawn # No output to stdout and no result (implicit None) mock_stdout = [ 'pre result line 1\n', '%(delimiter)sTrue%(delimiter)s' % values, 'post result line 1' ] mock_stderr = [ 'stderr line 1\n', 'stderr line 2\n', 'stderr line 3\n' ] mock_process = mock.Mock() mock_process.returncode = 0 mock_popen.return_value = mock_process mock_process.stdout.closed = False mock_process.stderr.closed = False mock_process.stdout.readline = make_mock_stream_readline(mock_process.stdout, mock_stdout, stop_counter=3) mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr, stop_counter=3) runner = self._get_mock_runner_obj() runner.entry_point = PASCAL_ROW_ACTION_PATH runner.pre_run() (_, output, _) = runner.run({'row_index': 4}) self.assertEqual(output['stdout'], 'pre result line 1\npost result line 1') self.assertEqual(output['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3\n') self.assertEqual(output['result'], 'True') self.assertEqual(output['exit_code'], 0) output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 0) # False is a default behavior so end result should be the same cfg.CONF.set_override(name='stream_output', group='actionrunner', override=False) mock_process = mock.Mock() mock_process.returncode = 0 mock_popen.return_value = mock_process mock_process.stdout.closed = False mock_process.stderr.closed = False mock_process.stdout.readline = make_mock_stream_readline(mock_process.stdout, mock_stdout, stop_counter=3) mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr, stop_counter=3) runner.pre_run() (_, output, _) = runner.run({'row_index': 4}) self.assertEqual(output['stdout'], 'pre result line 1\npost result line 1') self.assertEqual(output['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3\n') self.assertEqual(output['result'], 'True') self.assertEqual(output['exit_code'], 0) output_dbs = ActionExecutionOutput.get_all() self.assertEqual(len(output_dbs), 0)