Skip to content

Commit 7341ba6

Browse files
authored
[202411] Backport: [reboot-cause] Use UTC to ensure consistent sorting (#306)
[202411] Backport: [reboot-cause] Use UTC to ensure consistent sorting
1 parent bccad08 commit 7341ba6

File tree

4 files changed

+87
-2
lines changed

4 files changed

+87
-2
lines changed

scripts/determine-reboot-cause

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def main():
229229
previous_reboot_cause, additional_reboot_info = determine_reboot_cause()
230230

231231
# Current time
232-
reboot_cause_gen_time = str(datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S'))
232+
reboot_cause_gen_time = str(datetime.datetime.utcnow().strftime('%Y_%m_%d_%H_%M_%S'))
233233

234234
# Save the previous cause info into its history file as json format
235235
reboot_cause_dict = get_reboot_cause_dict(previous_reboot_cause, additional_reboot_info, reboot_cause_gen_time)

scripts/procdockerstatsd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ class ProcDockerStats(daemon_base.DaemonBase):
239239

240240
while True:
241241
self.update_dockerstats_command()
242-
datetimeobj = datetime.now()
242+
datetimeobj = datetime.utcnow()
243243
# Adding key to store latest update time.
244244
self.update_state_db('DOCKER_STATS|LastUpdateTime', 'lastupdate', str(datetimeobj))
245245
self.update_processstats_command()

tests/determine-reboot-cause_test.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import os
33
import shutil
44
import pytest
5+
import datetime
6+
import re
57

68
from swsscommon import swsscommon
79
from sonic_py_common.general import load_module_from_source
@@ -206,3 +208,21 @@ def test_determine_reboot_cause_main_with_reboot_cause_dir(self):
206208
determine_reboot_cause.main()
207209
assert os.path.exists("host/reboot-cause/reboot-cause.txt") == True
208210
assert os.path.exists("host/reboot-cause/previous-reboot-cause.json") == True
211+
212+
def test_reboot_cause_gen_time_is_utc(self):
213+
# Patch datetime.datetime.utcnow to return a known value
214+
class FixedDatetime(datetime.datetime):
215+
@classmethod
216+
def utcnow(cls):
217+
return cls(2024, 7, 1, 12, 34, 56)
218+
orig_datetime = determine_reboot_cause.datetime
219+
determine_reboot_cause.datetime = FixedDatetime
220+
221+
try:
222+
gen_time = determine_reboot_cause.datetime.utcnow().strftime('%Y_%m_%d_%H_%M_%S')
223+
# Should match the fixed UTC time
224+
assert gen_time == "2024_07_01_12_34_56"
225+
# Optionally, check the format
226+
assert re.match(r"\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}", gen_time)
227+
finally:
228+
determine_reboot_cause.datetime = orig_datetime

tests/procdockerstatsd_test.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,68 @@ def test_update_fipsstats_command(self, mock_cmd):
143143
pdstatsd.update_fipsstats_command()
144144
assert pdstatsd.state_db.get('STATE_DB', 'FIPS_STATS|state', 'enforced') == "False"
145145
assert pdstatsd.state_db.get('STATE_DB', 'FIPS_STATS|state', 'enabled') == "True"
146+
147+
def test_datetime_utcnow_usage(self):
148+
"""Test that datetime.utcnow() is used instead of datetime.now() for consistent UTC timestamps"""
149+
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
150+
151+
# Mock datetime.utcnow to return a fixed time for testing
152+
fixed_time = datetime(2025, 7, 1, 12, 34, 56)
153+
154+
with patch('procdockerstatsd.datetime') as mock_datetime:
155+
mock_datetime.utcnow.return_value = fixed_time
156+
157+
# Test the update_fipsstats_command method which uses datetime.utcnow()
158+
pdstatsd.update_fipsstats_command()
159+
160+
# Verify that utcnow() was called
161+
mock_datetime.utcnow.assert_called_once()
162+
163+
# Verify that now() was NOT called (ensuring we're using UTC)
164+
mock_datetime.now.assert_not_called()
165+
166+
# Test the main run loop datetime usage
167+
with patch.object(pdstatsd, 'update_dockerstats_command'):
168+
with patch.object(pdstatsd, 'update_processstats_command'):
169+
with patch.object(pdstatsd, 'update_fipsstats_command'):
170+
with patch('time.sleep'): # Prevent actual sleep
171+
# Mock the first iteration of the run loop
172+
pdstatsd.update_dockerstats_command()
173+
datetimeobj = mock_datetime.utcnow()
174+
pdstatsd.update_state_db('DOCKER_STATS|LastUpdateTime', 'lastupdate', str(datetimeobj))
175+
pdstatsd.update_processstats_command()
176+
pdstatsd.update_state_db('PROCESS_STATS|LastUpdateTime', 'lastupdate', str(datetimeobj))
177+
pdstatsd.update_fipsstats_command()
178+
pdstatsd.update_state_db('FIPS_STATS|LastUpdateTime', 'lastupdate', str(datetimeobj))
179+
180+
# Verify utcnow() was called multiple times as expected
181+
assert mock_datetime.utcnow.call_count >= 2
182+
183+
def test_run_method_executes_with_utcnow(self):
184+
"""Test that run method executes and uses datetime.utcnow()"""
185+
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
186+
187+
# Mock all dependencies but allow datetime.utcnow() to run normally
188+
with patch.object(pdstatsd, 'update_dockerstats_command'):
189+
with patch.object(pdstatsd, 'update_processstats_command'):
190+
with patch.object(pdstatsd, 'update_fipsstats_command'):
191+
with patch('time.sleep', side_effect=Exception("Stop after first iteration")):
192+
with patch('os.getuid', return_value=0): # Mock as root
193+
with patch.object(pdstatsd, 'log_info'):
194+
with patch.object(pdstatsd, 'update_state_db') as mock_update_db:
195+
# This will actually call run() method
196+
try:
197+
pdstatsd.run()
198+
except Exception as e:
199+
if "Stop after first iteration" in str(e):
200+
# Verify that update_state_db was called
201+
assert mock_update_db.call_count >= 3
202+
# Verify that timestamps were passed
203+
for call in mock_update_db.call_args_list:
204+
args = call[0]
205+
if len(args) >= 3 and 'lastupdate' in args[1]:
206+
timestamp_str = args[2]
207+
assert isinstance(timestamp_str, str)
208+
assert len(timestamp_str) > 0
209+
else:
210+
raise

0 commit comments

Comments
 (0)