From 63ba400fcd82fca0cfd616ced483bad95511230e Mon Sep 17 00:00:00 2001 From: Caio Bruchert Date: Fri, 25 Aug 2023 15:51:10 -0300 Subject: [PATCH] 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 --- .../common/helpers/constants.py | 3 + .../common/helpers/os_clock_monitor.py | 101 ++++++++++++++---- .../common/helpers/ptpsync.py | 20 +++- .../tests/test_os_clock_monitor.py | 4 +- .../common/helpers/constants.py | 2 + .../common/helpers/ptpsync.py | 20 +++- .../ptp-notification/templates/daemonset.yaml | 8 ++ .../helm-charts/ptp-notification/values.yaml | 3 + 8 files changed, 131 insertions(+), 30 deletions(-) diff --git a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/constants.py b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/constants.py index 2a57ad3..22c42af 100644 --- a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/constants.py +++ b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/constants.py @@ -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" diff --git a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/os_clock_monitor.py b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/os_clock_monitor.py index a108543..3097685 100644 --- a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/os_clock_monitor.py +++ b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/os_clock_monitor.py @@ -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() diff --git a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py index 5b659a3..00685ca 100644 --- a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py +++ b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py @@ -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 diff --git a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/tests/test_os_clock_monitor.py b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/tests/test_os_clock_monitor.py index df8a1f0..508be75 100644 --- a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/tests/test_os_clock_monitor.py +++ b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/tests/test_os_clock_monitor.py @@ -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'], diff --git a/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/constants.py b/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/constants.py index 4f28f3d..74312ca 100644 --- a/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/constants.py +++ b/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/constants.py @@ -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/' diff --git a/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py b/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py index fb89ca1..384bd87 100644 --- a/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py +++ b/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py @@ -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 diff --git a/stx-ptp-notification-helm/stx-ptp-notification-helm/helm-charts/ptp-notification/templates/daemonset.yaml b/stx-ptp-notification-helm/stx-ptp-notification-helm/helm-charts/ptp-notification/templates/daemonset.yaml index f2a9671..d8badfa 100644 --- a/stx-ptp-notification-helm/stx-ptp-notification-helm/helm-charts/ptp-notification/templates/daemonset.yaml +++ b/stx-ptp-notification-helm/stx-ptp-notification-helm/helm-charts/ptp-notification/templates/daemonset.yaml @@ -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 diff --git a/stx-ptp-notification-helm/stx-ptp-notification-helm/helm-charts/ptp-notification/values.yaml b/stx-ptp-notification-helm/stx-ptp-notification-helm/helm-charts/ptp-notification/values.yaml index cefabc1..59f95e3 100644 --- a/stx-ptp-notification-helm/stx-ptp-notification-helm/helm-charts/ptp-notification/values.yaml +++ b/stx-ptp-notification-helm/stx-ptp-notification-helm/helm-charts/ptp-notification/values.yaml @@ -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: