def test_action_stdout_and_stderr_is_stored_in_the_db( self, mock_spawn, mock_popen): # Feature is enabled cfg.CONF.set_override(name="stream_output", group="actionrunner", override=True) 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", "pre result line 2\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=4) 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.assertMultiLineEqual( output["stdout"], "pre result line 1\npre result line 2\npost result line 1") self.assertMultiLineEqual( output["stderr"], "stderr line 1\nstderr line 2\nstderr line 3\n") self.assertEqual(output["result"], "True") self.assertEqual(output["exit_code"], 0) # Verify stdout and stderr lines have been correctly stored in the db # Note - result delimiter should not be stored in the db output_dbs = ActionExecutionOutput.query(output_type="stdout") self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].runner_ref, "python-script") self.assertEqual(output_dbs[0].data, mock_stdout[0]) self.assertEqual(output_dbs[1].data, mock_stdout[1]) self.assertEqual(output_dbs[2].data, mock_stdout[3]) output_dbs = ActionExecutionOutput.query(output_type="stderr") self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].runner_ref, "python-script") self.assertEqual(output_dbs[0].data, mock_stderr[0]) self.assertEqual(output_dbs[1].data, mock_stderr[1]) self.assertEqual(output_dbs[2].data, mock_stderr[2])
def test_no_timestamp_doesnt_delete_things(self): now = date_utils.get_datetime_utc_now() exec_model = copy.deepcopy(self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = now - timedelta(days=15) exec_model['end_timestamp'] = now - timedelta(days=14) exec_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution(exec_model['id'], count=3) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3) expected_msg = 'Specify a valid timestamp' self.assertRaisesRegexp(ValueError, expected_msg, purge_executions, logger=LOG, timestamp=None) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3)
def test_no_timestamp_doesnt_delete_things(self): now = date_utils.get_datetime_utc_now() exec_model = copy.deepcopy( self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = now - timedelta(days=15) exec_model['end_timestamp'] = now - timedelta(days=14) exec_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution( exec_model['id'], count=3) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3) expected_msg = 'Specify a valid timestamp' self.assertRaisesRegexp(ValueError, expected_msg, purge_executions, logger=LOG, timestamp=None) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3)
def test_action_stdout_and_stderr_is_stored_in_the_db( self, mock_spawn, mock_popen): # Feature is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) 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', 'pre result line 2\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=4) 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.container_service = service.RunnerContainerService() runner.pre_run() (_, output, _) = runner.run({'row_index': 4}) self.assertEqual( output['stdout'], 'pre result line 1\npre result line 2\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) # Verify stdout and stderr lines have been correctly stored in the db # Note - result delimiter should not be stored in the db output_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].runner_ref, 'python-script') self.assertEqual(output_dbs[0].data, mock_stdout[0]) self.assertEqual(output_dbs[1].data, mock_stdout[1]) self.assertEqual(output_dbs[2].data, mock_stdout[3]) output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].runner_ref, 'python-script') self.assertEqual(output_dbs[0].data, mock_stderr[0]) self.assertEqual(output_dbs[1].data, mock_stderr[1]) self.assertEqual(output_dbs[2].data, mock_stderr[2])
def test_action_stdout_and_stderr_is_stored_in_the_db(self, mock_spawn, mock_popen): # Feature is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) 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', 'pre result line 2\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=4) 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\npre result line 2\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) # Verify stdout and stderr lines have been correctly stored in the db # Note - result delimiter should not be stored in the db output_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].runner_ref, 'python-script') self.assertEqual(output_dbs[0].data, mock_stdout[0]) self.assertEqual(output_dbs[1].data, mock_stdout[1]) self.assertEqual(output_dbs[2].data, mock_stdout[3]) output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].runner_ref, 'python-script') self.assertEqual(output_dbs[0].data, mock_stderr[0]) self.assertEqual(output_dbs[1].data, mock_stderr[1]) self.assertEqual(output_dbs[2].data, mock_stderr[2])
def test_action_stdout_and_stderr_is_stored_in_the_db( self, mock_spawn, mock_popen): # Feature is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) # 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 = [ 'stdout line 1\n', 'stdout line 2\n', ] 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=2) mock_process.stderr.readline = make_mock_stream_readline( mock_process.stderr, mock_stderr, stop_counter=3) 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 $ST2_ACTION_API_URL') runner.pre_run() status, result, _ = runner.run({}) runner.post_run(status, result) self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertEqual(result['stdout'], 'stdout line 1\nstdout line 2') self.assertEqual(result['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3') self.assertEqual(result['return_code'], 0) # Verify stdout and stderr lines have been correctly stored in the db output_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(output_dbs), 2) self.assertEqual(output_dbs[0].data, mock_stdout[0]) self.assertEqual(output_dbs[1].data, mock_stdout[1]) output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].data, mock_stderr[0]) self.assertEqual(output_dbs[1].data, mock_stderr[1]) self.assertEqual(output_dbs[2].data, mock_stderr[2])
def test_action_stdout_and_stderr_is_stored_in_the_db(self, mock_spawn, mock_popen): # Feature is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) # 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 = [ 'stdout line 1\n', 'stdout line 2\n', ] 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=2) mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr, stop_counter=3) 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 $ST2_ACTION_API_URL') runner.pre_run() status, result, _ = runner.run({}) runner.post_run(status, result) self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertEqual(result['stdout'], 'stdout line 1\nstdout line 2') self.assertEqual(result['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3') self.assertEqual(result['return_code'], 0) # Verify stdout and stderr lines have been correctly stored in the db output_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(output_dbs), 2) self.assertEqual(output_dbs[0].data, mock_stdout[0]) self.assertEqual(output_dbs[1].data, mock_stdout[1]) output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].data, mock_stderr[0]) self.assertEqual(output_dbs[1].data, mock_stderr[1]) self.assertEqual(output_dbs[2].data, mock_stderr[2])
def existing_output_iter(): # Consume and return all of the existing lines # pylint: disable=no-member output_dbs = ActionExecutionOutput.query(execution_id=execution_id, **query_filters) output = ''.join([output_db.data for output_db in output_dbs]) yield six.binary_type(output.encode('utf-8'))
def existing_output_iter(): # Consume and return all of the existing lines # pylint: disable=no-member output_dbs = ActionExecutionOutput.query(execution_id=execution_id, **query_filters) # Note: We return all at once instead of yield line by line to avoid multiple socket # writes and to achieve better performance output = ''.join([output_db.data for output_db in output_dbs]) yield six.binary_type(output.encode('utf-8'))
def existing_output_iter(): # Consume and return all of the existing lines output_dbs = ActionExecutionOutput.query(execution_id=execution_id, **query_filters) # Note: We return all at once instead of yield line by line to avoid multiple socket # writes and to achieve better performance output = [format_output_object(output_db) for output_db in output_dbs] output = ''.join(output) yield six.binary_type(output.encode('utf-8'))
def test_purge_executions_with_timestamp(self): now = date_utils.get_datetime_utc_now() # Write one execution after cut-off threshold exec_model = copy.deepcopy( self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = now - timedelta(days=15) exec_model['end_timestamp'] = now - timedelta(days=14) exec_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution( exec_model['id'], count=3) # Write one execution before cut-off threshold exec_model = copy.deepcopy( self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = now - timedelta(days=22) exec_model['end_timestamp'] = now - timedelta(days=21) exec_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution( exec_model['id'], count=3) execs = ActionExecution.get_all() self.assertEqual(len(execs), 2) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 6) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 6) purge_executions(logger=LOG, timestamp=now - timedelta(days=20)) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3)
def existing_output_iter(): # Consume and return all of the existing lines output_dbs = ActionExecutionOutput.query( execution_id=execution_id, **query_filters ) # Note: We return all at once instead of yield line by line to avoid multiple socket # writes and to achieve better performance output = [format_output_object(output_db) for output_db in output_dbs] output = "".join(output) yield six.binary_type(output.encode("utf-8"))
def test_purge_executions_with_timestamp(self): now = date_utils.get_datetime_utc_now() # Write one execution after cut-off threshold exec_model = copy.deepcopy(self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = now - timedelta(days=15) exec_model['end_timestamp'] = now - timedelta(days=14) exec_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution(exec_model['id'], count=3) # Write one execution before cut-off threshold exec_model = copy.deepcopy(self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = now - timedelta(days=22) exec_model['end_timestamp'] = now - timedelta(days=21) exec_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution(exec_model['id'], count=3) execs = ActionExecution.get_all() self.assertEqual(len(execs), 2) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 6) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 6) purge_executions(logger=LOG, timestamp=now - timedelta(days=20)) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3)
def test_purge_executions_with_action_ref(self): now = date_utils.get_datetime_utc_now() exec_model = copy.deepcopy( self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = now - timedelta(days=15) exec_model['end_timestamp'] = now - timedelta(days=14) exec_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution( exec_model['id'], count=3) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3) # Invalid action reference, nothing should be deleted purge_executions(logger=LOG, action_ref='core.localzzz', timestamp=now - timedelta(days=10)) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3) purge_executions(logger=LOG, action_ref='core.local', timestamp=now - timedelta(days=10)) execs = ActionExecution.get_all() self.assertEqual(len(execs), 0) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 0) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 0)
def test_purge_executions_with_action_ref(self): now = date_utils.get_datetime_utc_now() exec_model = copy.deepcopy(self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = now - timedelta(days=15) exec_model['end_timestamp'] = now - timedelta(days=14) exec_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution(exec_model['id'], count=3) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3) # Invalid action reference, nothing should be deleted purge_executions(logger=LOG, action_ref='core.localzzz', timestamp=now - timedelta(days=10)) execs = ActionExecution.get_all() self.assertEqual(len(execs), 1) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 3) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 3) purge_executions(logger=LOG, action_ref='core.local', timestamp=now - timedelta(days=10)) execs = ActionExecution.get_all() self.assertEqual(len(execs), 0) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 0) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 0)
def test_action_stdout_and_stderr_is_stored_in_the_db( self, mock_spawn, mock_popen): # Feature is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) # 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 = [ 'stdout line 1\n', 'stdout line 2\n', 'stdout line 3\n', 'stdout line 4\n' ] 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=4) mock_process.stderr.readline = make_mock_stream_readline( mock_process.stderr, mock_stderr, stop_counter=3) 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( result['stdout'], 'stdout line 1\nstdout line 2\nstdout line 3\nstdout line 4') self.assertEqual(result['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3') self.assertEqual(result['return_code'], 0) # Verify stdout and stderr lines have been correctly stored in the db output_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(output_dbs), 4) self.assertEqual(output_dbs[0].data, mock_stdout[0]) self.assertEqual(output_dbs[1].data, mock_stdout[1]) self.assertEqual(output_dbs[2].data, mock_stdout[2]) self.assertEqual(output_dbs[3].data, mock_stdout[3]) output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].data, mock_stderr[0]) self.assertEqual(output_dbs[1].data, mock_stderr[1]) self.assertEqual(output_dbs[2].data, mock_stderr[2])
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_stored_in_the_db_short_running_action( self, mock_spawn, mock_popen): # Verify that we correctly retrieve all the output and wait for stdout and stderr reading # threads for short running actions. models = self.fixtures_loader.load_models( fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']}) action_db = models['actions']['local.yaml'] # Feature is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) # 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 = ['stdout line 1\n', 'stdout line 2\n'] mock_stderr = ['stderr line 1\n', 'stderr line 2\n'] # We add a sleep to simulate action process exiting before we finish reading data from 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=2, sleep_delay=1) mock_process.stderr.readline = make_mock_stream_readline( mock_process.stderr, mock_stderr, stop_counter=2) for index in range(1, 4): mock_process.stdout.closed = False mock_process.stderr.closed = False mock_process.stdout.counter = 0 mock_process.stderr.counter = 0 runner = self._get_runner(action_db, cmd='echo "foobar"') runner.pre_run() status, result, _ = runner.run({}) self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertEqual(result['stdout'], 'stdout line 1\nstdout line 2') self.assertEqual(result['stderr'], 'stderr line 1\nstderr line 2') self.assertEqual(result['return_code'], 0) # Verify stdout and stderr lines have been correctly stored in the db output_dbs = ActionExecutionOutput.query(output_type='stdout') if index == 1: db_index_1 = 0 db_index_2 = 1 elif index == 2: db_index_1 = 2 db_index_2 = 3 elif index == 3: db_index_1 = 4 db_index_2 = 5 elif index == 4: db_index_1 = 6 db_index_2 = 7 self.assertEqual(len(output_dbs), (index * 2)) self.assertEqual(output_dbs[db_index_1].data, mock_stdout[0]) self.assertEqual(output_dbs[db_index_2].data, mock_stdout[1]) output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), (index * 2)) self.assertEqual(output_dbs[db_index_1].data, mock_stderr[0]) self.assertEqual(output_dbs[db_index_2].data, mock_stderr[1])
def test_action_stdout_and_stderr_is_stored_in_the_db_short_running_action(self, mock_spawn, mock_popen): # Verify that we correctly retrieve all the output and wait for stdout and stderr reading # threads for short running actions. models = self.fixtures_loader.load_models( fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']}) action_db = models['actions']['local.yaml'] # Feature is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) # 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 = [ 'stdout line 1\n', 'stdout line 2\n' ] mock_stderr = [ 'stderr line 1\n', 'stderr line 2\n' ] # We add a sleep to simulate action process exiting before we finish reading data from 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=2, sleep_delay=1) mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr, stop_counter=2) for index in range(1, 4): mock_process.stdout.closed = False mock_process.stderr.closed = False mock_process.stdout.counter = 0 mock_process.stderr.counter = 0 runner = self._get_runner(action_db, cmd='echo "foobar"') runner.pre_run() status, result, _ = runner.run({}) self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED) self.assertEqual(result['stdout'], 'stdout line 1\nstdout line 2') self.assertEqual(result['stderr'], 'stderr line 1\nstderr line 2') self.assertEqual(result['return_code'], 0) # Verify stdout and stderr lines have been correctly stored in the db output_dbs = ActionExecutionOutput.query(output_type='stdout') if index == 1: db_index_1 = 0 db_index_2 = 1 elif index == 2: db_index_1 = 2 db_index_2 = 3 elif index == 3: db_index_1 = 4 db_index_2 = 5 elif index == 4: db_index_1 = 6 db_index_2 = 7 self.assertEqual(len(output_dbs), (index * 2)) self.assertEqual(output_dbs[db_index_1].data, mock_stdout[0]) self.assertEqual(output_dbs[db_index_2].data, mock_stdout[1]) output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), (index * 2)) self.assertEqual(output_dbs[db_index_1].data, mock_stderr[0]) self.assertEqual(output_dbs[db_index_2].data, mock_stderr[1])
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_stored_in_the_db(self, mock_spawn, mock_popen): # Feature is enabled cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True) # 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 = [ 'stdout line 1\n', 'stdout line 2\n', 'stdout line 3\n', 'stdout line 4\n' ] 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=4) mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr, stop_counter=3) 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(result['stdout'], 'stdout line 1\nstdout line 2\nstdout line 3\nstdout line 4') self.assertEqual(result['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3') self.assertEqual(result['return_code'], 0) # Verify stdout and stderr lines have been correctly stored in the db output_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(output_dbs), 4) self.assertEqual(output_dbs[0].data, mock_stdout[0]) self.assertEqual(output_dbs[1].data, mock_stdout[1]) self.assertEqual(output_dbs[2].data, mock_stdout[2]) self.assertEqual(output_dbs[3].data, mock_stdout[3]) output_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].data, mock_stderr[0]) self.assertEqual(output_dbs[1].data, mock_stderr[1]) self.assertEqual(output_dbs[2].data, mock_stderr[2])
def test_purge_incomplete(self): now = date_utils.get_datetime_utc_now() start_ts = now - timedelta(days=15) # Write executions before cut-off threshold exec_model = copy.deepcopy( self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_SCHEDULED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution( exec_model['id'], count=1) exec_model = copy.deepcopy( self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_RUNNING exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution( exec_model['id'], count=1) exec_model = copy.deepcopy( self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_DELAYED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution( exec_model['id'], count=1) exec_model = copy.deepcopy( self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_CANCELING exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution( exec_model['id'], count=1) exec_model = copy.deepcopy( self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_REQUESTED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution( exec_model['id'], count=1) self.assertEqual(len(ActionExecution.get_all()), 5) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 5) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 5) # Incompleted executions shouldnt be purged purge_executions(logger=LOG, timestamp=now - timedelta(days=10), purge_incomplete=False) self.assertEqual(len(ActionExecution.get_all()), 5) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 5) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 5) purge_executions(logger=LOG, timestamp=now - timedelta(days=10), purge_incomplete=True) self.assertEqual(len(ActionExecution.get_all()), 0) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 0) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_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_garbage_collection(self): now = date_utils.get_datetime_utc_now() status = action_constants.LIVEACTION_STATUS_SUCCEEDED # Insert come mock ActionExecutionDB objects with start_timestamp < TTL defined in the # config old_executions_count = 15 ttl_days = 30 # > 20 timestamp = (now - datetime.timedelta(days=ttl_days)) for index in range(0, old_executions_count): action_execution_db = ActionExecutionDB( start_timestamp=timestamp, end_timestamp=timestamp, status=status, action={'ref': 'core.local'}, runner={'name': 'local-shell-cmd'}, liveaction={'ref': 'foo'}) ActionExecution.add_or_update(action_execution_db) stdout_db = ActionExecutionOutputDB(execution_id=str( action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stdout', data='stdout') ActionExecutionOutput.add_or_update(stdout_db) stderr_db = ActionExecutionOutputDB(execution_id=str( action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stderr', data='stderr') ActionExecutionOutput.add_or_update(stderr_db) # Insert come mock ActionExecutionDB objects with start_timestamp > TTL defined in the # config new_executions_count = 5 ttl_days = 2 # < 20 timestamp = (now - datetime.timedelta(days=ttl_days)) for index in range(0, new_executions_count): action_execution_db = ActionExecutionDB( start_timestamp=timestamp, end_timestamp=timestamp, status=status, action={'ref': 'core.local'}, runner={'name': 'local-shell-cmd'}, liveaction={'ref': 'foo'}) ActionExecution.add_or_update(action_execution_db) stdout_db = ActionExecutionOutputDB(execution_id=str( action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stdout', data='stdout') ActionExecutionOutput.add_or_update(stdout_db) stderr_db = ActionExecutionOutputDB(execution_id=str( action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stderr', data='stderr') ActionExecutionOutput.add_or_update(stderr_db) # Insert some mock output objects where start_timestamp > action_executions_output_ttl new_output_count = 5 ttl_days = 15 # > 10 and < 20 timestamp = (now - datetime.timedelta(days=ttl_days)) for index in range(0, new_output_count): action_execution_db = ActionExecutionDB( start_timestamp=timestamp, end_timestamp=timestamp, status=status, action={'ref': 'core.local'}, runner={'name': 'local-shell-cmd'}, liveaction={'ref': 'foo'}) ActionExecution.add_or_update(action_execution_db) stdout_db = ActionExecutionOutputDB(execution_id=str( action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stdout', data='stdout') ActionExecutionOutput.add_or_update(stdout_db) stderr_db = ActionExecutionOutputDB(execution_id=str( action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stderr', data='stderr') ActionExecutionOutput.add_or_update(stderr_db) execs = ActionExecution.get_all() self.assertEqual( len(execs), (old_executions_count + new_executions_count + new_output_count)) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual( len(stdout_dbs), (old_executions_count + new_executions_count + new_output_count)) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual( len(stderr_dbs), (old_executions_count + new_executions_count + new_output_count)) # Start garbage collector process = self._start_garbage_collector() # Give it some time to perform garbage collection and kill it eventlet.sleep(15) process.send_signal(signal.SIGKILL) self.remove_process(process=process) # Old executions and corresponding objects should have been garbage collected execs = ActionExecution.get_all() self.assertEqual(len(execs), (new_executions_count + new_output_count)) # Collection for output objects older than 10 days is also enabled, so those objects # should be deleted as well stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), (new_executions_count)) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), (new_executions_count))
def test_action_stdout_and_stderr_is_stored_in_the_db( self, mock_spawn, mock_popen): # Feature is enabled cfg.CONF.set_override(name="stream_output", group="actionrunner", override=True) # 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 = [ "stdout line 1\n", "stdout line 2\n", "stdout line 3\n", "stdout line 4\n", ] 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=4) mock_process.stderr.readline = make_mock_stream_readline( mock_process.stderr, mock_stderr, stop_counter=3) 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( result["stdout"], "stdout line 1\nstdout line 2\nstdout line 3\nstdout line 4", ) self.assertEqual(result["stderr"], "stderr line 1\nstderr line 2\nstderr line 3") self.assertEqual(result["return_code"], 0) # Verify stdout and stderr lines have been correctly stored in the db output_dbs = ActionExecutionOutput.query(output_type="stdout") self.assertEqual(len(output_dbs), 4) self.assertEqual(output_dbs[0].data, mock_stdout[0]) self.assertEqual(output_dbs[1].data, mock_stdout[1]) self.assertEqual(output_dbs[2].data, mock_stdout[2]) self.assertEqual(output_dbs[3].data, mock_stdout[3]) output_dbs = ActionExecutionOutput.query(output_type="stderr") self.assertEqual(len(output_dbs), 3) self.assertEqual(output_dbs[0].data, mock_stderr[0]) self.assertEqual(output_dbs[1].data, mock_stderr[1]) self.assertEqual(output_dbs[2].data, mock_stderr[2])
def test_purge_incomplete(self): now = date_utils.get_datetime_utc_now() start_ts = now - timedelta(days=15) # Write executions before cut-off threshold exec_model = copy.deepcopy(self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_SCHEDULED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution(exec_model['id'], count=1) exec_model = copy.deepcopy(self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_RUNNING exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution(exec_model['id'], count=1) exec_model = copy.deepcopy(self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_DELAYED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution(exec_model['id'], count=1) exec_model = copy.deepcopy(self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_CANCELING exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution(exec_model['id'], count=1) exec_model = copy.deepcopy(self.models['executions']['execution1.yaml']) exec_model['start_timestamp'] = start_ts exec_model['status'] = action_constants.LIVEACTION_STATUS_REQUESTED exec_model['id'] = bson.ObjectId() ActionExecution.add_or_update(exec_model) # Insert corresponding stdout and stderr db mock models self._insert_mock_stdout_and_stderr_objects_for_execution(exec_model['id'], count=1) self.assertEqual(len(ActionExecution.get_all()), 5) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 5) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 5) # Incompleted executions shouldnt be purged purge_executions(logger=LOG, timestamp=now - timedelta(days=10), purge_incomplete=False) self.assertEqual(len(ActionExecution.get_all()), 5) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 5) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 5) purge_executions(logger=LOG, timestamp=now - timedelta(days=10), purge_incomplete=True) self.assertEqual(len(ActionExecution.get_all()), 0) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), 0) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), 0)
def test_garbage_collection(self): now = date_utils.get_datetime_utc_now() status = action_constants.LIVEACTION_STATUS_SUCCEEDED # Insert come mock ActionExecutionDB objects with start_timestamp < TTL defined in the # config old_executions_count = 15 ttl_days = 30 # > 20 timestamp = (now - datetime.timedelta(days=ttl_days)) for index in range(0, old_executions_count): action_execution_db = ActionExecutionDB(start_timestamp=timestamp, end_timestamp=timestamp, status=status, action={'ref': 'core.local'}, runner={'name': 'local-shell-cmd'}, liveaction={'ref': 'foo'}) ActionExecution.add_or_update(action_execution_db) stdout_db = ActionExecutionOutputDB(execution_id=str(action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stdout', data='stdout') ActionExecutionOutput.add_or_update(stdout_db) stderr_db = ActionExecutionOutputDB(execution_id=str(action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stderr', data='stderr') ActionExecutionOutput.add_or_update(stderr_db) # Insert come mock ActionExecutionDB objects with start_timestamp > TTL defined in the # config new_executions_count = 5 ttl_days = 2 # < 20 timestamp = (now - datetime.timedelta(days=ttl_days)) for index in range(0, new_executions_count): action_execution_db = ActionExecutionDB(start_timestamp=timestamp, end_timestamp=timestamp, status=status, action={'ref': 'core.local'}, runner={'name': 'local-shell-cmd'}, liveaction={'ref': 'foo'}) ActionExecution.add_or_update(action_execution_db) stdout_db = ActionExecutionOutputDB(execution_id=str(action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stdout', data='stdout') ActionExecutionOutput.add_or_update(stdout_db) stderr_db = ActionExecutionOutputDB(execution_id=str(action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stderr', data='stderr') ActionExecutionOutput.add_or_update(stderr_db) # Insert some mock output objects where start_timestamp > action_executions_output_ttl new_output_count = 5 ttl_days = 15 # > 10 and < 20 timestamp = (now - datetime.timedelta(days=ttl_days)) for index in range(0, new_output_count): action_execution_db = ActionExecutionDB(start_timestamp=timestamp, end_timestamp=timestamp, status=status, action={'ref': 'core.local'}, runner={'name': 'local-shell-cmd'}, liveaction={'ref': 'foo'}) ActionExecution.add_or_update(action_execution_db) stdout_db = ActionExecutionOutputDB(execution_id=str(action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stdout', data='stdout') ActionExecutionOutput.add_or_update(stdout_db) stderr_db = ActionExecutionOutputDB(execution_id=str(action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stderr', data='stderr') ActionExecutionOutput.add_or_update(stderr_db) execs = ActionExecution.get_all() self.assertEqual(len(execs), (old_executions_count + new_executions_count + new_output_count)) stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), (old_executions_count + new_executions_count + new_output_count)) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), (old_executions_count + new_executions_count + new_output_count)) # Start garbage collector process = self._start_garbage_collector() # Give it some time to perform garbage collection and kill it eventlet.sleep(15) process.send_signal(signal.SIGKILL) self.remove_process(process=process) # Old executions and corresponding objects should have been garbage collected execs = ActionExecution.get_all() self.assertEqual(len(execs), (new_executions_count + new_output_count)) # Collection for output objects older than 10 days is also enabled, so those objects # should be deleted as well stdout_dbs = ActionExecutionOutput.query(output_type='stdout') self.assertEqual(len(stdout_dbs), (new_executions_count)) stderr_dbs = ActionExecutionOutput.query(output_type='stderr') self.assertEqual(len(stderr_dbs), (new_executions_count))