Skip to content

Commit 18ebd7b

Browse files
committed
Improve hostcfgd to listen for new NTP config
Signed-off-by: Yevhen Fastiuk <[email protected]>
1 parent 508d642 commit 18ebd7b

File tree

1 file changed

+133
-67
lines changed

1 file changed

+133
-67
lines changed

scripts/hostcfgd

+133-67
Original file line numberDiff line numberDiff line change
@@ -1302,84 +1302,141 @@ class NtpCfg(object):
13021302
1) ntp-config.service handles the configuration updates and then starts ntp.service
13031303
2) Both of them start after all the feature services start
13041304
3) Purpose of this daemon is to propagate runtime config changes in
1305-
NTP, NTP_SERVER and LOOPBACK_INTERFACE
1305+
NTP, NTP_SERVER, NTP_KEY, and LOOPBACK_INTERFACE
13061306
"""
1307+
NTP_CONF_RESTART = ['service', 'ntp-config', 'restart']
1308+
13071309
def __init__(self):
1308-
self.ntp_global = {}
1309-
self.ntp_servers = set()
1310+
self.cache = {}
1311+
1312+
def load(self, ntp_global_conf: dict, ntp_server_conf: dict,
1313+
ntp_key_conf: dict):
1314+
"""Load initial NTP configuration
13101315
1311-
def load(self, ntp_global_conf, ntp_server_conf):
1312-
syslog.syslog(syslog.LOG_INFO, "NtpCfg load ...")
1316+
Force load cache on init. NTP config should be taken at boot-time by
1317+
ntp and ntp-config services. So loading whole config here.
1318+
1319+
Args:
1320+
ntp_global_conf: Global configuration
1321+
ntp_server_conf: Servers configuration
1322+
ntp_key_conf: Keys configuration
1323+
"""
13131324

1314-
for row in ntp_global_conf:
1315-
self.ntp_global_update(row, ntp_global_conf[row], is_load=True)
1325+
syslog.syslog(syslog.LOG_INFO, "NtpCfg: load initial")
13161326

1317-
# Force reload on init
1318-
self.ntp_server_update(0, None, is_load=True)
1327+
if ntp_global_conf is None:
1328+
ntp_global_conf = {}
1329+
1330+
# Force load cache on init.
1331+
# NTP config should be taken at boot-time by ntp and ntp-config
1332+
# services.
1333+
self.cache = {
1334+
'global': ntp_global_conf.get('global', {}),
1335+
'servers': ntp_server_conf,
1336+
'keys': ntp_key_conf
1337+
}
13191338

13201339
def handle_ntp_source_intf_chg(self, intf_name):
1321-
# if no ntp server configured, do nothing
1322-
if not self.ntp_servers:
1340+
# If no ntp server configured, do nothing. Source interface will be
1341+
# taken once any server will be configured.
1342+
if not self.cache.get('servers'):
13231343
return
13241344

13251345
# check only the intf configured as source interface
1326-
if intf_name not in self.ntp_global.get('src_intf', '').split(';'):
1346+
ifs = self.cache.get('global', {}).get('src_intf', '').split(';')
1347+
if intf_name not in ifs:
1348+
return
1349+
1350+
# Just restart ntp config
1351+
try:
1352+
run_cmd(self.NTP_CONF_RESTART, True, True)
1353+
except Exception:
1354+
syslog.syslog(syslog.LOG_ERR, 'NtpCfg: Failed to restart '
1355+
'ntp-config service')
1356+
return
1357+
1358+
def ntp_global_update(self, key: str, data: dict):
1359+
"""Update NTP global configuration
1360+
1361+
The table holds NTP global configuration. It has some configs that
1362+
require reloading only but some other require restarting ntp daemon.
1363+
Handle each of them accordingly.
1364+
1365+
Args:
1366+
key: Triggered table's key. Should be always "global"
1367+
data: Global configuration data
1368+
"""
1369+
1370+
syslog.syslog(syslog.LOG_NOTICE, 'NtpCfg: Global configuration update')
1371+
if key != 'global' or self.cache.get('global', {}) == data:
1372+
syslog.syslog(syslog.LOG_NOTICE, 'NtpCfg: Nothing to update')
13271373
return
1328-
else:
1329-
# just restart ntp config
1330-
cmd = ['systemctl', 'restart', 'ntp-config']
1331-
run_cmd(cmd)
13321374

1333-
def ntp_global_update(self, key, data, is_load=False):
1334-
syslog.syslog(syslog.LOG_INFO, 'NTP GLOBAL Update')
1335-
orig_src = self.ntp_global.get('src_intf', '')
1336-
orig_src_set = set(orig_src.split(";"))
1337-
orig_vrf = self.ntp_global.get('vrf', '')
1375+
syslog.syslog(syslog.LOG_INFO, f'NtpCfg: Set global config: {data}')
13381376

1339-
new_src = data.get('src_intf', '')
1340-
new_src_set = set(new_src.split(";"))
1341-
new_vrf = data.get('vrf', '')
1377+
old_dhcp = self.cache.get(key, {}).get('dhcp')
1378+
old_vrf = self.cache.get(key, {}).get('vrf')
1379+
new_dhcp = data.get('dhcp')
1380+
new_vrf = data.get('vrf')
1381+
1382+
restart_ntpd = False
1383+
if new_dhcp != old_dhcp or new_vrf != old_vrf:
1384+
restart_ntpd = True
1385+
1386+
# Restarting the service
1387+
try:
1388+
run_cmd(self.NTP_CONF_RESTART, True, True)
1389+
if restart_ntpd:
1390+
run_cmd(['service', 'ntp', 'restart'], True, True)
1391+
except Exception:
1392+
syslog.syslog(syslog.LOG_ERR, f'NtpCfg: Failed to restart ntp '
1393+
'services')
1394+
return
13421395

13431396
# Update the Local Cache
1344-
self.ntp_global = data
1397+
self.cache[key] = data
1398+
1399+
def ntp_srv_key_update(self, ntp_servers: dict, ntp_keys: dict):
1400+
"""Update NTP server/key configuration
13451401
1346-
# If initial load don't restart daemon
1347-
if is_load: return
1402+
The tables holds only NTP servers config and/or NTP authentication keys
1403+
config, so any change to those tables should cause NTP config reload.
1404+
It does not make sense to handle each of the separately. NTP config
1405+
reload takes whole configuration, so once got to this handler - cache
1406+
whole config.
13481407
1349-
# check if ntp server configured, if not, do nothing
1350-
if not self.ntp_servers:
1351-
syslog.syslog(syslog.LOG_INFO, "No ntp server when global config change, do nothing")
1408+
Args:
1409+
ntp_servers: Servers config table
1410+
ntp_keys: Keys config table
1411+
"""
1412+
1413+
syslog.syslog(syslog.LOG_NOTICE, 'NtpCfg: Server/key configuration '
1414+
'update')
1415+
1416+
if (self.cache.get('servers', {}) == ntp_servers and
1417+
self.cache.get('keys', {}) == ntp_keys):
1418+
syslog.syslog(syslog.LOG_NOTICE, 'NtpCfg: Nothing to update')
13521419
return
13531420

1354-
if orig_src_set != new_src_set:
1355-
syslog.syslog(syslog.LOG_INFO, "ntp global update for source intf old {} new {}, restarting ntp-config"
1356-
.format(orig_src_set, new_src_set))
1357-
cmd = ['systemctl', 'restart', 'ntp-config']
1358-
run_cmd(cmd)
1359-
elif new_vrf != orig_vrf:
1360-
syslog.syslog(syslog.LOG_INFO, "ntp global update for vrf old {} new {}, restarting ntp service"
1361-
.format(orig_vrf, new_vrf))
1362-
cmd = ['service', 'ntp', 'restart']
1363-
run_cmd(cmd)
1421+
# Pop keys values to print
1422+
ntp_keys_print = copy.deepcopy(ntp_keys)
1423+
for key in ntp_keys_print:
1424+
ntp_keys_print[key].pop('value', None)
13641425

1365-
def ntp_server_update(self, key, op, is_load=False):
1366-
syslog.syslog(syslog.LOG_INFO, 'ntp server update key {}'.format(key))
1367-
1368-
restart_config = False
1369-
if not is_load:
1370-
if op == "SET" and key not in self.ntp_servers:
1371-
restart_config = True
1372-
self.ntp_servers.add(key)
1373-
elif op == "DEL" and key in self.ntp_servers:
1374-
restart_config = True
1375-
self.ntp_servers.remove(key)
1376-
else:
1377-
restart_config = True
1426+
syslog.syslog(syslog.LOG_INFO, f'NtpCfg: Set servers: {ntp_servers}')
1427+
syslog.syslog(syslog.LOG_INFO, f'NtpCfg: Set keys: {ntp_keys_print}')
13781428

1379-
if restart_config:
1380-
cmd = ['systemctl', 'restart', 'ntp-config']
1381-
syslog.syslog(syslog.LOG_INFO, 'ntp server update, restarting ntp-config, ntp servers configured {}'.format(self.ntp_servers))
1382-
run_cmd(cmd)
1429+
# Restarting the service
1430+
try:
1431+
run_cmd(self.NTP_CONF_RESTART, True, True)
1432+
except Exception:
1433+
syslog.syslog(syslog.LOG_ERR, f'NtpCfg: Failed to restart '
1434+
'ntp-config service')
1435+
return
1436+
1437+
# Updating the cache
1438+
self.cache['servers'] = ntp_servers
1439+
self.cache['keys'] = ntp_keys
13831440

13841441
class PamLimitsCfg(object):
13851442
"""
@@ -1721,26 +1778,26 @@ class HostConfigDaemon:
17211778
radius_global = init_data['RADIUS']
17221779
radius_server = init_data['RADIUS_SERVER']
17231780
lpbk_table = init_data['LOOPBACK_INTERFACE']
1724-
ntp_server = init_data['NTP_SERVER']
1725-
ntp_global = init_data['NTP']
17261781
kdump = init_data['KDUMP']
17271782
passwh = init_data['PASSW_HARDENING']
17281783
dev_meta = init_data.get(swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, {})
17291784
mgmt_ifc = init_data.get(swsscommon.CFG_MGMT_INTERFACE_TABLE_NAME, {})
17301785
mgmt_vrf = init_data.get(swsscommon.CFG_MGMT_VRF_CONFIG_TABLE_NAME, {})
17311786
syslog_cfg = init_data.get(swsscommon.CFG_SYSLOG_CONFIG_TABLE_NAME, {})
17321787
syslog_srv = init_data.get(swsscommon.CFG_SYSLOG_SERVER_TABLE_NAME, {})
1733-
1788+
ntp_global = init_data.get(swsscommon.CFG_NTP_GLOBAL_TABLE_NAME)
1789+
ntp_servers = init_data.get(swsscommon.CFG_NTP_SERVER_TABLE_NAME)
1790+
ntp_keys = init_data.get(swsscommon.CFG_NTP_KEY_TABLE_NAME)
17341791

17351792
self.feature_handler.sync_state_field(features)
17361793
self.aaacfg.load(aaa, tacacs_global, tacacs_server, radius_global, radius_server)
17371794
self.iptables.load(lpbk_table)
1738-
self.ntpcfg.load(ntp_global, ntp_server)
17391795
self.kdumpCfg.load(kdump)
17401796
self.passwcfg.load(passwh)
17411797
self.devmetacfg.load(dev_meta)
17421798
self.mgmtifacecfg.load(mgmt_ifc, mgmt_vrf)
17431799
self.rsyslogcfg.load(syslog_cfg, syslog_srv)
1800+
self.ntpcfg.load(ntp_global, ntp_servers, ntp_keys)
17441801

17451802
# Update AAA with the hostname
17461803
self.aaacfg.hostname_update(self.devmetacfg.hostname)
@@ -1826,12 +1883,16 @@ class HostConfigDaemon:
18261883
key = ConfigDBConnector.deserialize_key(key)
18271884
self.aaacfg.handle_radius_source_intf_ip_chg(key)
18281885

1829-
def ntp_server_handler(self, key, op, data):
1830-
self.ntpcfg.ntp_server_update(key, op)
1831-
18321886
def ntp_global_handler(self, key, op, data):
1887+
syslog.syslog(syslog.LOG_NOTICE, 'Handling NTP global config')
18331888
self.ntpcfg.ntp_global_update(key, data)
18341889

1890+
def ntp_srv_key_handler(self, key, op, data):
1891+
syslog.syslog(syslog.LOG_NOTICE, 'Handling NTP server/key config')
1892+
self.ntpcfg.ntp_srv_key_update(
1893+
self.config_db.get_table(swsscommon.CFG_NTP_SERVER_TABLE_NAME),
1894+
self.config_db.get_table(swsscommon.CFG_NTP_KEY_TABLE_NAME))
1895+
18351896
def kdump_handler (self, key, op, data):
18361897
syslog.syslog(syslog.LOG_INFO, 'Kdump handler...')
18371898
self.kdumpCfg.kdump_update(key, data)
@@ -1885,9 +1946,6 @@ class HostConfigDaemon:
18851946
self.config_db.subscribe('PASSW_HARDENING', make_callback(self.passwh_handler))
18861947
# Handle IPTables configuration
18871948
self.config_db.subscribe('LOOPBACK_INTERFACE', make_callback(self.lpbk_handler))
1888-
# Handle NTP & NTP_SERVER updates
1889-
self.config_db.subscribe('NTP', make_callback(self.ntp_global_handler))
1890-
self.config_db.subscribe('NTP_SERVER', make_callback(self.ntp_server_handler))
18911949
# Handle updates to src intf changes in radius
18921950
self.config_db.subscribe('MGMT_INTERFACE', make_callback(self.mgmt_intf_handler))
18931951
self.config_db.subscribe('VLAN_INTERFACE', make_callback(self.vlan_intf_handler))
@@ -1909,6 +1967,14 @@ class HostConfigDaemon:
19091967
self.config_db.subscribe(swsscommon.CFG_SYSLOG_SERVER_TABLE_NAME,
19101968
make_callback(self.rsyslog_server_handler))
19111969

1970+
# Handle NTP, NTP_SERVER, and NTP_KEY updates
1971+
self.config_db.subscribe(swsscommon.CFG_NTP_GLOBAL_TABLE_NAME,
1972+
make_callback(self.ntp_global_handler))
1973+
self.config_db.subscribe(swsscommon.CFG_NTP_SERVER_TABLE_NAME,
1974+
make_callback(self.ntp_srv_key_handler))
1975+
self.config_db.subscribe(swsscommon.CFG_NTP_KEY_TABLE_NAME,
1976+
make_callback(self.ntp_srv_key_handler))
1977+
19121978
syslog.syslog(syslog.LOG_INFO,
19131979
"Waiting for systemctl to finish initialization")
19141980
self.wait_till_system_init_done()

0 commit comments

Comments
 (0)