[ntp]: Source interface support for NTP (#6033)
Added source interface support for NTP. Also made NTP start on Mgmt-VRF by default when configured. **- How I did it** 1) Updated hostcfg to listen to global config NTP and NTP_SERVER tables and restart ntp when ever the configuration changes. NTP table includes source interface configuration. 2) The ntp script updated to by default start on Mgmt-VFT when configured. Signed-off-by: Prabhu Sreenivasan <prabhu.sreenivasan@broadcom>
This commit is contained in:
parent
0a36de3a89
commit
df2a4ded98
@ -322,6 +322,8 @@ echo "ntp-config.service" | sudo tee -a $GENERATED_SERVICE_FILE
|
||||
sudo cp $IMAGE_CONFIGS/ntp/ntp-config.sh $FILESYSTEM_ROOT/usr/bin/
|
||||
sudo cp $IMAGE_CONFIGS/ntp/ntp.conf.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
|
||||
sudo cp $IMAGE_CONFIGS/ntp/ntp-systemd-wrapper $FILESYSTEM_ROOT/usr/lib/ntp/
|
||||
sudo cp $IMAGE_CONFIGS/ntp/ntp.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM
|
||||
echo "ntp.service" | sudo tee -a $GENERATED_SERVICE_FILE
|
||||
|
||||
# Copy warmboot-finalizer files
|
||||
sudo LANG=C cp $IMAGE_CONFIGS/warmboot-finalizer/finalize-warmboot.sh $FILESYSTEM_ROOT/usr/local/bin/finalize-warmboot.sh
|
||||
|
@ -50,11 +50,20 @@ case $1 in
|
||||
fi
|
||||
(
|
||||
flock -w 180 9
|
||||
|
||||
# when mgmt vrf is configured, ntp starts in mgmt vrf by default unless user configures otherwise
|
||||
vrfEnabled=$(/usr/local/bin/sonic-cfggen -d -v 'MGMT_VRF_CONFIG["vrf_global"]["mgmtVrfEnabled"]' 2> /dev/null)
|
||||
vrfConfigured=$(/usr/local/bin/sonic-cfggen -d -v 'NTP["global"]["vrf"]' 2> /dev/null)
|
||||
if [ "$vrfEnabled" = "true" ]
|
||||
then
|
||||
log_daemon_msg "Starting NTP server in mgmt-vrf" "ntpd"
|
||||
cgexec -g l3mdev:mgmt start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
|
||||
if [ "$vrfConfigured" = "default" ]
|
||||
then
|
||||
log_daemon_msg "Starting NTP server in default-vrf for default set as NTP vrf" "ntpd"
|
||||
start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
|
||||
else
|
||||
log_daemon_msg "Starting NTP server in mgmt-vrf" "ntpd"
|
||||
cgexec -g l3mdev:mgmt start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
|
||||
fi
|
||||
else
|
||||
log_daemon_msg "Starting NTP server in default-vrf" "ntpd"
|
||||
start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
|
||||
|
@ -3,6 +3,7 @@ Description=Update NTP configuration
|
||||
Requires=updategraph.service
|
||||
After=updategraph.service
|
||||
Before=ntp.service
|
||||
StartLimitIntervalSec=0
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
|
@ -27,11 +27,21 @@ fi
|
||||
|
||||
(
|
||||
flock -w 180 9
|
||||
# when mgmt vrf is configured, ntp starts in mgmt vrf by default unless user configures otherwise
|
||||
vrfEnabled=$(/usr/local/bin/sonic-cfggen -d -v 'MGMT_VRF_CONFIG["vrf_global"]["mgmtVrfEnabled"]' 2> /dev/null)
|
||||
vrfConfigured=$(/usr/local/bin/sonic-cfggen -d -v 'NTP["global"]["vrf"]' 2> /dev/null)
|
||||
if [ "$vrfEnabled" = "true" ]
|
||||
then
|
||||
ip vrf exec mgmt start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
|
||||
if [ "$vrfConfigured" = "default" ]
|
||||
then
|
||||
log_daemon_msg "Starting NTP server in default-vrf for default set as NTP vrf" "ntpd"
|
||||
start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
|
||||
else
|
||||
log_daemon_msg "Starting NTP server in mgmt-vrf" "ntpd"
|
||||
ip vrf exec mgmt start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
|
||||
fi
|
||||
else
|
||||
log_daemon_msg "Starting NTP server in default-vrf" "ntpd"
|
||||
start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
|
||||
fi
|
||||
) 9>$LOCKFILE
|
||||
|
@ -31,10 +31,49 @@ filegen clockstats file clockstats type day enable
|
||||
server {{ ntp_server }} iburst
|
||||
{% endfor %}
|
||||
|
||||
#listen on source interface if configured, else
|
||||
#only listen on MGMT_INTERFACE, LOOPBACK_INTERFACE ip when MGMT_INTERFACE is not defined, or eth0
|
||||
# if we don't have both of them (default is to listen on all ip addresses)
|
||||
interface ignore wildcard
|
||||
{% if MGMT_INTERFACE %}
|
||||
|
||||
# set global variable for configured source interface name
|
||||
# set global boolean to indicate if the ip of the configured source interface is configured
|
||||
# if the source interface is configured but no ip on that interface, then listen on another
|
||||
# interface based on existing logic
|
||||
{%- macro check_ip_on_interface(interface_name, table_name) %}
|
||||
{%- if table_name %}
|
||||
{%- for (name, source_prefix) in table_name|pfx_filter %}
|
||||
{%- if source_prefix and name == interface_name %}
|
||||
true
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% set ns = namespace(source_intf = "") %}
|
||||
{% set ns = namespace(source_intf_ip = 'false') %}
|
||||
{% if (NTP) and (NTP['global']['src_intf']) %}
|
||||
{% set ns.source_intf = (NTP['global']['src_intf']) %}
|
||||
{% if ns.source_intf != "" %}
|
||||
{% if ns.source_intf == "eth0" %}
|
||||
{% set ns.source_intf_ip = 'true' %}
|
||||
{% elif ns.source_intf.startswith('Vlan') %}
|
||||
{% set ns.source_intf_ip = check_ip_on_interface(ns.source_intf, VLAN_INTERFACE) %}
|
||||
{% elif ns.source_intf.startswith('Ethernet') %}
|
||||
{% set ns.source_intf_ip = check_ip_on_interface(ns.source_intf, INTERFACE) %}
|
||||
{% elif ns.source_intf.startswith('PortChannel') %}
|
||||
{% set ns.source_intf_ip = check_ip_on_interface(ns.source_intf, PORTCHANNEL_INTERFACE) %}
|
||||
{% elif ns.source_intf.startswith('Loopback') %}
|
||||
{% set ns.source_intf_ip = check_ip_on_interface(ns.source_intf, LOOPBACK_INTERFACE) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if ns.source_intf_ip == 'true' %}
|
||||
interface listen {{ns.source_intf}}
|
||||
{% elif (NTP) and NTP['global']['vrf'] == 'mgmt' %}
|
||||
interface listen eth0
|
||||
{% elif MGMT_INTERFACE %}
|
||||
{% for (mgmt_intf, mgmt_prefix) in MGMT_INTERFACE|pfx_filter %}
|
||||
interface listen {{ mgmt_prefix | ip }}
|
||||
{% endfor %}
|
||||
|
16
files/image_config/ntp/ntp.service
Normal file
16
files/image_config/ntp/ntp.service
Normal file
@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Network Time Service
|
||||
Documentation=man:ntpd(8)
|
||||
After=network.target
|
||||
Conflicts=systemd-timesyncd.service
|
||||
StartLimitIntervalSec=0
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
# Debian uses a shell wrapper to process /etc/default/ntp
|
||||
# and select DHCP-provided NTP servers if available
|
||||
ExecStart=/usr/lib/ntp/ntp-systemd-wrapper
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -23,6 +23,13 @@ TACPLUS_SERVER_PASSKEY_DEFAULT = ""
|
||||
TACPLUS_SERVER_TIMEOUT_DEFAULT = "5"
|
||||
TACPLUS_SERVER_AUTH_TYPE_DEFAULT = "pap"
|
||||
|
||||
def run_cmd(cmd, log_err = True):
|
||||
try:
|
||||
subprocess.check_call(cmd, shell = True)
|
||||
except Exception as err:
|
||||
if log_err:
|
||||
syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}"
|
||||
.format(err.cmd, err.returncode, err.output))
|
||||
|
||||
def is_true(val):
|
||||
if val == 'True' or val == 'true':
|
||||
@ -283,6 +290,104 @@ class KdumpCfg(object):
|
||||
if isLoad or data.get("num_dumps") is not None:
|
||||
run_cmd("sonic-kdump-config --num_dumps " + num_dumps)
|
||||
|
||||
class NtpCfg(object):
|
||||
def __init__(self, CfgDb):
|
||||
self.config_db = CfgDb
|
||||
self.ntp_global = {}
|
||||
self.has_ntp_servers = False
|
||||
|
||||
def load(self, ntp_global_conf, ntp_server_conf):
|
||||
syslog.syslog(syslog.LOG_INFO, "NtpCfg load ...")
|
||||
|
||||
for row in ntp_global_conf:
|
||||
self.ntp_global_update(row, ntp_global_conf[row], True)
|
||||
|
||||
self.ntp_server_update(0, ntp_server_conf, True)
|
||||
|
||||
def handle_ntp_source_intf_chg (self, key):
|
||||
# if no ntp server configured, do nothing
|
||||
if self.has_ntp_servers == False:
|
||||
return
|
||||
|
||||
# check only the intf configured as source interface
|
||||
if (len(self.ntp_global) == 0):
|
||||
return
|
||||
|
||||
if 'src_intf' not in self.ntp_global:
|
||||
return
|
||||
|
||||
if key[0] != self.ntp_global['src_intf']:
|
||||
return
|
||||
else:
|
||||
# just restart ntp config
|
||||
cmd = 'systemctl restart ntp-config'
|
||||
run_cmd(cmd)
|
||||
|
||||
def ntp_global_update(self, key, data, isLoad):
|
||||
syslog.syslog(syslog.LOG_INFO, "ntp global configuration update")
|
||||
|
||||
new_src = new_vrf = orig_src = orig_vrf = ""
|
||||
|
||||
if 'src_intf' in data:
|
||||
new_src = data['src_intf']
|
||||
|
||||
if 'vrf' in data:
|
||||
new_vrf = data['vrf']
|
||||
|
||||
if (len(self.ntp_global) != 0):
|
||||
|
||||
if 'src_intf' in self.ntp_global:
|
||||
orig_src = self.ntp_global['src_intf']
|
||||
|
||||
if 'vrf' in self.ntp_global:
|
||||
orig_vrf = self.ntp_global['vrf']
|
||||
|
||||
self.ntp_global = data
|
||||
|
||||
# during initial load of ntp configuration, ntp server configuration decides if to restart ntp-config
|
||||
if (isLoad):
|
||||
syslog.syslog(syslog.LOG_INFO, "ntp global update in load")
|
||||
return
|
||||
|
||||
# check if ntp server configured, if not, do nothing
|
||||
if self.has_ntp_servers == False:
|
||||
syslog.syslog(syslog.LOG_INFO, "no ntp server when global config change, do nothing")
|
||||
return
|
||||
|
||||
if (new_src != orig_src):
|
||||
syslog.syslog(syslog.LOG_INFO, "ntp global update for source intf old {} new {}, restarting ntp-config"
|
||||
.format(orig_src, new_src))
|
||||
cmd = 'systemctl restart ntp-config'
|
||||
run_cmd(cmd)
|
||||
else:
|
||||
if (new_vrf != orig_vrf):
|
||||
syslog.syslog(syslog.LOG_INFO, "ntp global update for vrf old {} new {}, restarting ntp service"
|
||||
.format(orig_vrf, new_vrf))
|
||||
cmd = 'service ntp restart'
|
||||
run_cmd(cmd)
|
||||
|
||||
def ntp_server_update(self, key, data, isLoad):
|
||||
syslog.syslog(syslog.LOG_INFO, 'ntp server update key {} data {}'.format(key, data))
|
||||
|
||||
# during load, restart ntp-config regardless if ntp server is configured or not
|
||||
if isLoad == True:
|
||||
if data != {}:
|
||||
self.has_ntp_servers = True
|
||||
else:
|
||||
# for runtime ntp server change, to determine if there is ntp server configured, need to
|
||||
# get from configDB, as delete triggers 2 event handling
|
||||
ntp_servers_tbl = self.config_db.get_table('NTP_SERVER')
|
||||
if ntp_servers_tbl != {}:
|
||||
self.has_ntp_servers = True
|
||||
else:
|
||||
self.has_ntp_servers = False
|
||||
|
||||
cmd = 'systemctl restart ntp-config'
|
||||
syslog.syslog(syslog.LOG_INFO, 'ntp server update, restarting ntp-config, ntp server exists {}'.format(self.has_ntp_servers))
|
||||
|
||||
run_cmd(cmd)
|
||||
|
||||
|
||||
class HostConfigDaemon:
|
||||
def __init__(self):
|
||||
self.config_db = ConfigDBConnector()
|
||||
@ -291,6 +396,7 @@ class HostConfigDaemon:
|
||||
|
||||
self.aaacfg = AaaCfg()
|
||||
self.iptables = Iptables()
|
||||
self.ntpcfg = NtpCfg(self.config_db)
|
||||
# Cache the values of 'state' field in 'FEATURE' table of each container
|
||||
self.cached_feature_states = {}
|
||||
|
||||
@ -310,6 +416,10 @@ class HostConfigDaemon:
|
||||
lpbk_table = self.config_db.get_table('LOOPBACK_INTERFACE')
|
||||
self.iptables.load(lpbk_table)
|
||||
|
||||
# Load NTP configurations
|
||||
ntp_server = self.config_db.get_table('NTP_SERVER')
|
||||
ntp_global = self.config_db.get_table('NTP')
|
||||
self.ntpcfg.load(ntp_global, ntp_server)
|
||||
|
||||
def update_feature_state(self, feature_name, state, feature_table):
|
||||
if self.cached_feature_states[feature_name] == "always_enabled":
|
||||
@ -425,6 +535,7 @@ class HostConfigDaemon:
|
||||
add = False
|
||||
|
||||
self.iptables.iptables_handler(key, data, add)
|
||||
self.ntpcfg.handle_ntp_source_intf_chg(key)
|
||||
|
||||
def feature_state_handler(self, key, data):
|
||||
feature_name = key
|
||||
@ -444,6 +555,16 @@ class HostConfigDaemon:
|
||||
if self.cached_feature_states[feature_name] != state:
|
||||
self.update_feature_state(feature_name, state, feature_table)
|
||||
|
||||
def ntp_server_handler (self, key, data):
|
||||
syslog.syslog(syslog.LOG_INFO, 'NTP server handler...')
|
||||
ntp_server_db = self.config_db.get_table('NTP_SERVER')
|
||||
data = ntp_server_db
|
||||
self.ntpcfg.ntp_server_update(key, data, False)
|
||||
|
||||
def ntp_global_handler (self, key, data):
|
||||
syslog.syslog(syslog.LOG_INFO, 'NTP global handler...')
|
||||
self.ntpcfg.ntp_global_update(key, data, False)
|
||||
|
||||
def kdump_handler (self, key, data):
|
||||
syslog.syslog(syslog.LOG_INFO, 'Kdump handler...')
|
||||
self.kdumpCfg.kdump_update(key, data, False)
|
||||
@ -462,6 +583,8 @@ class HostConfigDaemon:
|
||||
self.config_db.subscribe('TACPLUS', lambda table, key, data: self.tacacs_global_handler(key, data))
|
||||
self.config_db.subscribe('LOOPBACK_INTERFACE', lambda table, key, data: self.lpbk_handler(key, data))
|
||||
self.config_db.subscribe('FEATURE', lambda table, key, data: self.feature_state_handler(key, data))
|
||||
self.config_db.subscribe('NTP_SERVER', lambda table, key, data: self.ntp_server_handler(key, data))
|
||||
self.config_db.subscribe('NTP', lambda table, key, data: self.ntp_global_handler(key, data))
|
||||
self.config_db.subscribe('KDUMP', lambda table, key, data: self.kdump_handler(key, data))
|
||||
|
||||
syslog.syslog(syslog.LOG_INFO,
|
||||
|
Reference in New Issue
Block a user