From 912347abc3c7260ed5cb6a51df210427243f8d5b Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Fri, 26 Jan 2018 10:05:06 -0800 Subject: [PATCH 1/2] Include the duration when a state does not run, for example when the `onchanges` requisite is not met. --- salt/state.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/salt/state.py b/salt/state.py index 17a57a51a7..a3b238ecfd 100644 --- a/salt/state.py +++ b/salt/state.py @@ -162,6 +162,23 @@ def _l_tag(name, id_): return _gen_tag(low) +def _calculate_fake_duration(): + ''' + Generate a NULL duration for when states do not run + but we want the results to be consistent. + ''' + utc_start_time = datetime.datetime.utcnow() + local_start_time = utc_start_time - \ + (datetime.datetime.utcnow() - datetime.datetime.now()) + utc_finish_time = datetime.datetime.utcnow() + start_time = local_start_time.time().isoformat() + delta = (utc_finish_time - utc_start_time) + # duration in milliseconds.microseconds + duration = (delta.seconds * 1000000 + delta.microseconds)/1000.0 + + return start_time, duration + + def trim_req(req): ''' Trim any function off of a requisite @@ -2401,9 +2418,12 @@ class State(object): _cmt = 'One or more requisite failed: {0}'.format( ', '.join(str(i) for i in failed_requisites) ) + start_time, duration = _calculate_fake_duration() running[tag] = { 'changes': {}, 'result': False, + 'duration': duration, + 'start_time': start_time, 'comment': _cmt, '__run_num__': self.__run_num, '__sls__': low['__sls__'] @@ -2419,8 +2439,11 @@ class State(object): ret = self.call(low, chunks, running) running[tag] = ret elif status == 'pre': + start_time, duration = _calculate_fake_duration() pre_ret = {'changes': {}, 'result': True, + 'duration': duration, + 'start_time': start_time, 'comment': 'No changes detected', '__run_num__': self.__run_num, '__sls__': low['__sls__']} @@ -2428,15 +2451,21 @@ class State(object): self.pre[tag] = pre_ret self.__run_num += 1 elif status == 'onfail': + start_time, duration = _calculate_fake_duration() running[tag] = {'changes': {}, 'result': True, + 'duration': duration, + 'start_time': start_time, 'comment': 'State was not run because onfail req did not change', '__run_num__': self.__run_num, '__sls__': low['__sls__']} self.__run_num += 1 elif status == 'onchanges': + start_time, duration = _calculate_fake_duration() running[tag] = {'changes': {}, 'result': True, + 'duration': duration, + 'start_time': start_time, 'comment': 'State was not run because none of the onchanges reqs changed', '__run_num__': self.__run_num, '__sls__': low['__sls__']} From 359265869fa0b9ab6b5d1aa8b0d62890adf69564 Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Fri, 26 Jan 2018 12:04:04 -0800 Subject: [PATCH 2/2] Adding a couple tests to ensure that duration is included in state run results even when states do not run. --- salt/state.py | 3 +++ tests/integration/modules/test_state.py | 26 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/salt/state.py b/salt/state.py index a3b238ecfd..9ba668fd3b 100644 --- a/salt/state.py +++ b/salt/state.py @@ -2334,8 +2334,11 @@ class State(object): run_dict = self.pre else: run_dict = running + start_time, duration = _calculate_fake_duration() run_dict[tag] = {'changes': {}, 'result': False, + 'duration': duration, + 'start_time': start_time, 'comment': comment, '__run_num__': self.__run_num, '__sls__': low['__sls__']} diff --git a/tests/integration/modules/test_state.py b/tests/integration/modules/test_state.py index 591a946315..2d8a494d8b 100644 --- a/tests/integration/modules/test_state.py +++ b/tests/integration/modules/test_state.py @@ -1016,6 +1016,20 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): expected_result = 'State was not run because none of the onchanges reqs changed' self.assertIn(expected_result, test_data) + def test_onchanges_requisite_with_duration(self): + ''' + Tests a simple state using the onchanges requisite + the state will not run but results will include duration + ''' + + # Only run the state once and keep the return data + state_run = self.run_function('state.sls', mods='requisites.onchanges_simple') + + # Then, test the result of the state run when changes are not expected to happen + # and ensure duration is included in the results + test_data = state_run['cmd_|-test_non_changing_state_|-echo "Should not run"_|-run'] + self.assertIn('duration', test_data) + # onfail tests def test_onfail_requisite(self): @@ -1069,6 +1083,18 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): expected_result = 'State was not run because onfail req did not change' self.assertIn(expected_result, test_data) + def test_onfail_requisite_with_duration(self): + ''' + Tests a simple state using the onfail requisite + ''' + + # Only run the state once and keep the return data + state_run = self.run_function('state.sls', mods='requisites.onfail_simple') + + # Then, test the result of the state run when a failure is not expected to happen + test_data = state_run['cmd_|-test_non_failing_state_|-echo "Should not run"_|-run'] + self.assertIn('duration', test_data) + # listen tests def test_listen_requisite(self):