Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clock] Update system timezone via ConfigDB #57

Merged
merged 4 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions scripts/hostcfgd
Original file line number Diff line number Diff line change
Expand Up @@ -1369,12 +1369,16 @@ class DeviceMetaCfg(object):

def __init__(self):
self.hostname = ''
self.timezone = None

def load(self, dev_meta={}):
# Get hostname initial
self.hostname = dev_meta.get('localhost', {}).get('hostname', '')
syslog.syslog(syslog.LOG_DEBUG, f'Initial hostname: {self.hostname}')

# Load appropriate config
self.timezone = dev_meta.get('localhost', {}).get('timezone')

def hostname_update(self, data):
"""
Apply hostname handler.
Expand Down Expand Up @@ -1405,6 +1409,35 @@ class DeviceMetaCfg(object):
return
run_cmd(['sudo', 'monit', 'reload'])

def timezone_update(self, data):
"""
Apply timezone handler.
Run the following command in Linux: timedatectl set-timezone <timezone>
Args:
data: Read table's key's data.
"""
new_timezone = data.get('timezone')
syslog.syslog(syslog.LOG_DEBUG,
f'DeviceMetaCfg: timezone update to {new_timezone}')

if new_timezone is None:
syslog.syslog(syslog.LOG_DEBUG,
f'DeviceMetaCfg: Recieved empty timezone')
return

if new_timezone == self.timezone:
syslog.syslog(syslog.LOG_DEBUG,
f'DeviceMetaCfg: No change in timezone')
return

# run command will print out log error in case of error
run_cmd(['timedatectl', 'set-timezone', new_timezone])
self.timezone = new_timezone

run_cmd(['systemctl', 'restart', 'rsyslog'], True, False)
syslog.syslog(syslog.LOG_INFO, 'DeviceMetaCfg: Restart rsyslog after '
'changing timezone')

class MgmtIfaceCfg(object):
"""
MgmtIfaceCfg Config Daemon
Expand Down Expand Up @@ -1741,6 +1774,7 @@ class HostConfigDaemon:
def device_metadata_handler(self, key, op, data):
syslog.syslog(syslog.LOG_INFO, 'DeviceMeta handler...')
self.devmetacfg.hostname_update(data)
self.devmetacfg.timezone_update(data)

def syslog_handler(self, key, op, data):
self.syslogcfg.syslog_update(data)
Expand Down
53 changes: 29 additions & 24 deletions tests/hostcfgd/hostcfgd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,15 @@ def test_loopback_update(self):
class TestHostcfgdDaemon(TestCase):

def setUp(self):
self.get_dev_meta = mock.patch(
'sonic_py_common.device_info.get_device_runtime_metadata',
return_value={'DEVICE_RUNTIME_METADATA': {}})
self.get_dev_meta.start()
MockConfigDb.set_config_db(HOSTCFG_DAEMON_CFG_DB)

def tearDown(self):
MockConfigDb.CONFIG_DB = {}
self.get_dev_meta.stop()

@patchfs
def test_feature_events(self, fs):
Expand Down Expand Up @@ -392,6 +397,7 @@ def test_devicemeta_event(self):
"""
Test handling DEVICE_METADATA events.
1) Hostname reload
1) Timezone reload
"""
MockConfigDb.set_config_db(HOSTCFG_DAEMON_CFG_DB)
MockConfigDb.event_queue = [(swsscommon.CFG_DEVICE_METADATA_TABLE_NAME,
Expand All @@ -403,19 +409,16 @@ def test_devicemeta_event(self):
daemon.load(HOSTCFG_DAEMON_INIT_CFG_DB)
daemon.register_callbacks()
with mock.patch('hostcfgd.subprocess') as mocked_subprocess:
popen_mock = mock.Mock()
attrs = {'communicate.return_value': ('output', 'error')}
popen_mock.configure_mock(**attrs)
mocked_subprocess.Popen.return_value = popen_mock

try:
daemon.start()
except TimeoutError:
pass

expected = [
call(['sudo', 'service', 'hostname-config', 'restart']),
call(['sudo', 'monit', 'reload'])
call(['sudo', 'monit', 'reload']),
call(['timedatectl', 'set-timezone', 'Europe/Kyiv']),
call(['systemctl', 'restart', 'rsyslog']),
]
mocked_subprocess.check_call.assert_has_calls(expected,
any_order=True)
Expand All @@ -425,31 +428,33 @@ def test_devicemeta_event(self):
original_syslog = hostcfgd.syslog
MockConfigDb.set_config_db(HOSTCFG_DAEMON_CFG_DB)
with mock.patch('hostcfgd.syslog') as mocked_syslog:
mocked_syslog.LOG_ERR = original_syslog.LOG_ERR
try:
daemon.start()
except TimeoutError:
pass
with mock.patch('hostcfgd.subprocess') as mocked_subprocess:
mocked_syslog.LOG_ERR = original_syslog.LOG_ERR
try:
daemon.start()
except TimeoutError:
pass

expected = [
call(original_syslog.LOG_ERR, 'Hostname was not updated: Empty not allowed')
]
mocked_syslog.syslog.assert_has_calls(expected)
expected = [
call(original_syslog.LOG_ERR, 'Hostname was not updated: Empty not allowed')
]
mocked_syslog.syslog.assert_has_calls(expected)

daemon.devmetacfg.hostname = "SameHostName"
HOSTCFG_DAEMON_CFG_DB["DEVICE_METADATA"]["localhost"]["hostname"] = daemon.devmetacfg.hostname
MockConfigDb.set_config_db(HOSTCFG_DAEMON_CFG_DB)
with mock.patch('hostcfgd.syslog') as mocked_syslog:
mocked_syslog.LOG_INFO = original_syslog.LOG_INFO
try:
daemon.start()
except TimeoutError:
pass
with mock.patch('hostcfgd.subprocess') as mocked_subprocess:
mocked_syslog.LOG_INFO = original_syslog.LOG_INFO
try:
daemon.start()
except TimeoutError:
pass

expected = [
call(original_syslog.LOG_INFO, 'Hostname was not updated: Already set up with the same name: SameHostName')
]
mocked_syslog.syslog.assert_has_calls(expected)
expected = [
call(original_syslog.LOG_INFO, 'Hostname was not updated: Already set up with the same name: SameHostName')
]
mocked_syslog.syslog.assert_has_calls(expected)
fastiuk marked this conversation as resolved.
Show resolved Hide resolved

def test_mgmtiface_event(self):
"""
Expand Down
6 changes: 4 additions & 2 deletions tests/hostcfgd/test_vectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1182,7 +1182,8 @@
"LOOPBACK_INTERFACE": {},
"DEVICE_METADATA": {
"localhost": {
"hostname": "old-hostname"
"hostname": "old-hostname",
"timezone": "Etc/UTC"
}
},
"MGMT_INTERFACE": {},
Expand Down Expand Up @@ -1251,7 +1252,8 @@
"localhost": {
"subtype": "DualToR",
"type": "ToRRouter",
"hostname": "SomeNewHostname"
"hostname": "SomeNewHostname",
"timezone": "Europe/Kyiv"
}
},
"MGMT_INTERFACE": {
Expand Down