Make ptp-notification parameters configurable

To allow more flexibility to the user, The following PTP parameters
are being exposed to Helm Charts overrides configuration:

ptp-notification-v1:
- ptp4lClockClassLockedList

ptp-notification-v2:
- ptp4lClockClassLockedList
- phc2sysToleranceThreshold

Test Plan:
PASS: Build containers and helm charts
PASS: Deploy ptp-notification
PASS: v1 api - test ptp4lClockClassLockedList with the default,
               non-default and invalid lists
PASS: v2 api - test ptp4lClockClassLockedList with the default,
               non-default and invalid lists
PASS: v2 api - test phc2sysToleranceThreshold with commandline option
PASS: v2 api - test phc2sysToleranceThreshold with config file

Closes-bug: 2033539

Change-Id: I7dc915d99a91ff405c4cca4f1e1c1bf3a3559a4e
Signed-off-by: Caio Bruchert <caio.bruchert@windriver.com>
This commit is contained in:
Caio Bruchert 2023-08-25 15:51:10 -03:00
parent 0dae44b92d
commit 63ba400fcd
8 changed files with 131 additions and 30 deletions

View File

@ -28,6 +28,8 @@ TIME_IS_TRACEABLE1 = "1"
TIME_IS_TRACEABLE2 = "true"
GM_IS_PRESENT = "true"
CLOCK_CLASS_VALUE6 = "6"
CLOCK_CLASS_VALUE7 = "7"
CLOCK_CLASS_LOCKED_LIST = [CLOCK_CLASS_VALUE6, CLOCK_CLASS_VALUE7]
# ts2phc constants
NMEA_SERIALPORT = "ts2phc.nmea_serialport"
GNSS_PIN = "GNSS-1PPS"
@ -54,6 +56,7 @@ CLOCK_REALTIME = "CLOCK_REALTIME"
PHC2SYS_TOLERANCE_LOW = 36999999000
PHC2SYS_TOLERANCE_HIGH = 37000001000
PHC2SYS_TOLERANCE_THRESHOLD = 1000
PTP_V1_KEY = "ptp_notification_v1"

View File

@ -19,6 +19,8 @@ from trackingfunctionsdk.model.dto.osclockstate import OsClockState
LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG)
PLUGIN_STATUS_QUERY_EXEC = '/usr/sbin/pmc'
class OsClockMonitor:
@ -34,6 +36,16 @@ class OsClockMonitor:
self.phc2sys_ha_enabled = False
self.phc2sys_com_socket = None
self.phc2sys_tolerance_low = constants.PHC2SYS_TOLERANCE_LOW
self.phc2sys_tolerance_high = constants.PHC2SYS_TOLERANCE_HIGH
self.phc2sys_tolerance_threshold = constants.PHC2SYS_TOLERANCE_THRESHOLD
try:
self.phc2sys_tolerance_threshold = int(os.environ.get('PHC2SYS_TOLERANCE_THRESHOLD',
self.phc2sys_tolerance_threshold))
except:
LOG.error('Unable to convert PHC2SYS_TOLERANCE_THRESHOLD to integer,'
' using the default.')
self.set_phc2sys_instance()
"""Normally initialize all fields, but allow these to be skipped to
@ -52,6 +64,7 @@ class OsClockMonitor:
self.set_phc2sys_ha_interface_and_phc
else:
self.get_os_clock_time_source()
self.set_utc_offset()
self.get_os_clock_offset()
self.set_os_clock_state()
@ -111,10 +124,53 @@ class OsClockMonitor:
LOG.debug("Phc2sys HA interface: %s ptp_device: %s" % (self.phc_interface, self.ptp_device))
def set_utc_offset(self, pidfile_path="/var/run/"):
# Check command line options for offset
utc_offset = self._get_phc2sys_command_line_option(pidfile_path, '-O')
# If not, check config file for uds_address and domainNumber
# If uds_address, get utc_offset from TIME_PROPERTIES_DATA_SET using the phc2sys config
if not utc_offset:
utc_offset = constants.UTC_OFFSET
utc_offset_valid = False
if self.config.has_section('global') \
and 'domainNumber' in self.config['global'].keys() \
and 'uds_address' in self.config['global'].keys():
#
# sudo /usr/sbin/pmc -u -b 0 'GET TIME_PROPERTIES_DATA_SET'
#
data = subprocess.check_output(
[PLUGIN_STATUS_QUERY_EXEC, '-f', self.phc2sys_config, '-u', '-b', '0',
'GET TIME_PROPERTIES_DATA_SET']).decode()
for line in data.split('\n'):
if 'currentUtcOffset ' in line:
utc_offset = line.split()[1]
if 'currentUtcOffsetValid ' in line:
utc_offset_valid = bool(int(line.split()[1]))
if not utc_offset_valid:
utc_offset = constants.UTC_OFFSET
LOG.warning('currentUtcOffsetValid is %s, using the default currentUtcOffset %s'
% (utc_offset_valid, utc_offset))
utc_offset_nanoseconds = abs(int(utc_offset)) * 1000000000
self.phc2sys_tolerance_low = utc_offset_nanoseconds - self.phc2sys_tolerance_threshold
self.phc2sys_tolerance_high = utc_offset_nanoseconds + self.phc2sys_tolerance_threshold
LOG.debug('utc_offset_nanoseconds is %s, phc2sys_tolerance_threshold is %s'
% (utc_offset_nanoseconds, self.phc2sys_tolerance_threshold))
LOG.info('phc2sys_tolerance_low is %s, phc2sys_tolerance_high is %s'
% (self.phc2sys_tolerance_low, self.phc2sys_tolerance_high))
def get_os_clock_time_source(self, pidfile_path="/var/run/"):
"""Determine which PHC is disciplining the OS clock"""
self.phc_interface = None
self.phc_interface = self._check_command_line_interface(pidfile_path)
self.phc_interface = self._get_phc2sys_command_line_option(pidfile_path, '-s')
if self.phc_interface == constants.CLOCK_REALTIME:
LOG.info("PHC2SYS is using CLOCK_REALTIME, OS Clock is not being "
"disciplined by a PHC")
self.phc_interface = None
if self.phc_interface is None:
self.phc_interface = self._check_config_file_interface()
if self.phc_interface is None:
@ -123,30 +179,30 @@ class OsClockMonitor:
else:
self.ptp_device = self._get_interface_phc_device()
def _check_command_line_interface(self, pidfile_path):
def _get_phc2sys_command_line_option(self, pidfile_path, flag):
pidfile = pidfile_path + "phc2sys-" + self.phc2sys_instance + ".pid"
with open(pidfile, 'r') as f:
pid = f.readline().strip()
# Get command line params
cmdline_file = "/host/proc/" + pid + "/cmdline"
with open(cmdline_file, 'r') as f:
cmdline_args = f.readline().strip()
cmdline_args = cmdline_args.split("\x00")
# The interface will be at the index after "-s"
try:
interface_index = cmdline_args.index('-s')
except ValueError as ex:
LOG.error("No interface found in cmdline args. %s" % ex)
with open(pidfile, 'r') as f:
pid = f.readline().strip()
# Get command line params
cmdline_file = "/host/proc/" + pid + "/cmdline"
with open(cmdline_file, 'r') as f:
cmdline_args = f.readline().strip()
cmdline_args = cmdline_args.split("\x00")
except OSError as ex:
LOG.warning("Cannot open file. %s" % ex)
return None
phc_interface = cmdline_args[interface_index + 1]
if phc_interface == constants.CLOCK_REALTIME:
LOG.info("PHC2SYS is using CLOCK_REALTIME, OS Clock is not being "
"disciplined by a PHC")
# The option value will be at the index after the flag
try:
index = cmdline_args.index(flag)
except ValueError as ex:
LOG.debug("Flag not found in cmdline args. %s" % ex)
return None
LOG.debug("PHC interface is %s" % phc_interface)
return phc_interface
value = cmdline_args[index + 1]
LOG.debug("%s value is %s" % (flag, value))
return value
def _check_config_file_interface(self):
with open(self.phc2sys_config, 'r') as f:
@ -220,8 +276,8 @@ class OsClockMonitor:
def set_os_clock_state(self):
offset_int = int(self.offset)
if offset_int > constants.PHC2SYS_TOLERANCE_HIGH or \
offset_int < constants.PHC2SYS_TOLERANCE_LOW:
if offset_int > self.phc2sys_tolerance_high or \
offset_int < self.phc2sys_tolerance_low:
LOG.warning("PHC2SYS offset is outside of tolerance, "
"handling state change.")
self._state = OsClockState.Freerun
@ -241,6 +297,7 @@ class OsClockMonitor:
time_in_holdover = round(current_time - event_time)
max_holdover_time = (holdover_time - freq * 2)
self.set_utc_offset()
self.get_os_clock_offset()
self.set_os_clock_state()

View File

@ -23,6 +23,15 @@ LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG)
ptp4l_clock_class_locked = constants.CLOCK_CLASS_LOCKED_LIST
try:
tmp = os.environ.get('PTP4L_CLOCK_CLASS_LOCKED_LIST', ','.join(ptp4l_clock_class_locked))
ptp4l_clock_class_locked = sorted([str(int(e)) for e in tmp.split(',')])
except:
LOG.error('Unable to convert PTP4L_CLOCK_CLASS_LOCKED_LIST to a list of integers,'
' using the default.')
# run subprocess and returns out, err, errcode
def run_shell2(dir, ctx, args):
cwd = os.getcwd()
@ -82,11 +91,16 @@ def check_results(result, total_ptp_keywords, port_count):
break
else:
sync_state = constants.FREERUN_PHC_STATE
if (result[constants.TIME_TRACEABLE] != constants.TIME_IS_TRACEABLE1
# We can only expect timeTraceable=1 to be set when the clockClass list is the default.
# If the user has elected to override the Locked clockClasses, then it is necessary
# to ignore the timeTraceable property and define the lock state based only on the
# configured clockClasses.
if (ptp4l_clock_class_locked == constants.CLOCK_CLASS_LOCKED_LIST
and result[constants.TIME_TRACEABLE] != constants.TIME_IS_TRACEABLE1
and result[constants.TIME_TRACEABLE].lower != constants.TIME_IS_TRACEABLE2):
sync_state = constants.FREERUN_PHC_STATE
if (result[constants.GM_CLOCK_CLASS] not in
[constants.CLOCK_CLASS_VALUE6]):
if (result[constants.GM_CLOCK_CLASS] not in ptp4l_clock_class_locked):
sync_state = constants.FREERUN_PHC_STATE
return sync_state

View File

@ -49,14 +49,14 @@ class OsClockMonitorTests(unittest.TestCase):
"/ptpinstance/phc2sys-phc "
"-inst1.conf\x00-w\x00-s\x00ens1f0\x00").return_value)
mo.side_effect = handlers
self.assertEqual(self.clockmon._check_command_line_interface("/var/run/"), "ens1f0")
self.assertEqual(self.clockmon._get_phc2sys_command_line_option("/var/run/", "-s"), "ens1f0")
# Failure path - no interface in command line params
handlers = (mo.return_value,
mock_open(read_data="/usr/sbin/phc2sys\x00-f\x00/etc/ptpinstance/phc2sys-phc"
"-inst1.conf\x00-w\x00").return_value)
mo.side_effect = handlers
self.assertEqual(self.clockmon._check_command_line_interface("/var/run/"), None)
self.assertEqual(self.clockmon._get_phc2sys_command_line_option("/var/run/", "-s"), None)
@mock.patch('trackingfunctionsdk.common.helpers.os_clock_monitor.glob',
side_effect=[['/hostsys/class/net/ens1f0/device/ptp/ptp0'],

View File

@ -26,6 +26,8 @@ TIME_IS_TRACEABLE1 = "1"
TIME_IS_TRACEABLE2 = "true"
GM_IS_PRESENT = "true"
CLOCK_CLASS_VALUE6 = "6"
CLOCK_CLASS_VALUE7 = "7"
CLOCK_CLASS_LOCKED_LIST = [CLOCK_CLASS_VALUE6, CLOCK_CLASS_VALUE7]
if path.exists('/ptp/linuxptp/ptpinstance'):
LINUXPTP_CONFIG_PATH = '/ptp/linuxptp/ptpinstance/'

View File

@ -42,6 +42,15 @@ phc2sys_com_socket = os.environ.get('PHC2SYS_COM_SOCKET', "false")
phc2sys_config_file_path = '%sphc2sys-%s.conf' % (constants.LINUXPTP_CONFIG_PATH,
phc2sys_service_name)
ptp4l_clock_class_locked = constants.CLOCK_CLASS_LOCKED_LIST
try:
tmp = os.environ.get('PTP4L_CLOCK_CLASS_LOCKED_LIST', ','.join(ptp4l_clock_class_locked))
ptp4l_clock_class_locked = sorted([str(int(e)) for e in tmp.split(',')])
except:
LOG.error('Unable to convert PTP4L_CLOCK_CLASS_LOCKED_LIST to a list of integers,'
' using the default.')
# run subprocess and returns out, err, errcode
def run_shell2(dir, ctx, args):
cwd = os.getcwd()
@ -129,11 +138,16 @@ def check_results(result, total_ptp_keywords, port_count):
break
else:
sync_state = constants.FREERUN_PHC_STATE
if (result[constants.TIME_TRACEABLE] != constants.TIME_IS_TRACEABLE1
# We can only expect timeTraceable=1 to be set when the clockClass list is the default.
# If the user has elected to override the Locked clockClasses, then it is necessary
# to ignore the timeTraceable property and define the lock state based only on the
# configured clockClasses.
if (ptp4l_clock_class_locked == constants.CLOCK_CLASS_LOCKED_LIST
and result[constants.TIME_TRACEABLE] != constants.TIME_IS_TRACEABLE1
and result[constants.TIME_TRACEABLE].lower != constants.TIME_IS_TRACEABLE2):
sync_state = constants.FREERUN_PHC_STATE
if (result[constants.GM_CLOCK_CLASS] not in
[constants.CLOCK_CLASS_VALUE6]):
if (result[constants.GM_CLOCK_CLASS] not in ptp4l_clock_class_locked):
sync_state = constants.FREERUN_PHC_STATE
return sync_state

View File

@ -134,8 +134,14 @@ spec:
value: "registration.{{.Values.global.namespace}}.svc.cluster.local"
- name: PTP4L_SERVICE_NAME
value: "{{ .Values.ptptrackingv2.ptp4lServiceName }}"
- name: PTP4L_CLOCK_CLASS_LOCKED_LIST
value: "{{ .Values.ptptrackingv2.ptp4lClockClassLockedList }}"
- name: PTP4L_UTC_OFFSET
value: "{{ .Values.ptptrackingv2.ptp4lUtcOffset }}"
- name: PHC2SYS_SERVICE_NAME
value: "{{ .Values.ptptrackingv2.phc2sysServiceName }}"
- name: PHC2SYS_TOLERANCE_THRESHOLD
value: "{{ .Values.ptptrackingv2.phc2sysToleranceThreshold }}"
- name: TS2PHC_SERVICE_NAME
value: "{{ .Values.ptptrackingv2.ts2phcServiceName }}"
- name: LOGGING_LEVEL
@ -217,6 +223,8 @@ spec:
value: "registration.{{.Values.global.namespace}}.svc.cluster.local"
- name: PTP4L_SERVICE_NAME
value: "{{ .Values.ptptracking.ptp4lServiceName }}"
- name: PTP4L_CLOCK_CLASS_LOCKED_LIST
value: "{{ .Values.ptptracking.ptp4lClockClassLockedList }}"
- name: PHC2SYS_SERVICE_NAME
value: "{{ .Values.ptptracking.phc2sysServiceName }}"
- name: PHC2SYS_COM_SOCKET

View File

@ -70,6 +70,7 @@ ptptracking:
imagePullSecrets: default-registry-key
ptp4lSocket: /var/run/ptp4l-ptp4l-legacy
ptp4lServiceName: ptp4l-legacy
ptp4lClockClassLockedList: "6,7"
phc2sysServiceName: phc2sys-legacy
phc2sysComSocket: False
logging_level: INFO
@ -87,7 +88,9 @@ ptptrackingv2:
imagePullSecrets: default-registry-key
ptp4lSocket: /var/run/ptp4l-ptp4l-legacy
ptp4lServiceName: True
ptp4lClockClassLockedList: "6,7"
phc2sysServiceName: True
phc2sysToleranceThreshold: 1000
ts2phcServiceName: True
log_level: INFO
image: