Update ptp-notification phc2sys HA clock source tracking
Check for the presence of a phc2sys source clock when phc2sys HA mode is enabled and set lock state accordingly if no source is found. For ptp-notification-v2, this change affects the os clock state tracking. If HA mode is enabled and no clock source is present, proceed to transition to HOLDOVER and FREERUN states as required. For ptp-notification-v1, there has never been support for checking whether phc2sys is synced/locked, only whether the service is running. In order to leave the default behaviour the same, a user supplied helm override PHC2SYS_COM_SOCKET can be set. By providing a path to the phc2sys socket, ptp-notification-v1 will now check to see if a source clock is present for phc2sys and transition to HOLDOVER and FREERUN if no source is found. When the PHC2SYS_COM_SOCKET value is not provided, ptp-notification-v1 maintains the default behaviour and does not check for the presence of a source clock. Test Plan: PASS: Build containers and helm charts PASS: Deploy ptp-notification PASS: v2 api - test state transition when source clock is lost/returns PASS: v1 api - validate that default behaviour is unchanged when phc2sys com socket is not set PASS: v1 api - verify state transition when phc2sys source clock is lost/returns Story: 2010723 Task: 48514 Signed-off-by: Cole Walker <cole.walker@windriver.com> Change-Id: I769d36df024d207bf0f70efd11d68d8a8809f713
This commit is contained in:
parent
af89f51505
commit
0dae44b92d
@ -3,11 +3,13 @@
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import configparser
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
from glob import glob
|
||||
|
||||
from trackingfunctionsdk.common.helpers import log_helper
|
||||
@ -19,23 +21,37 @@ log_helper.config_logger(LOG)
|
||||
|
||||
|
||||
class OsClockMonitor:
|
||||
_state = OsClockState()
|
||||
last_event_time = None
|
||||
phc2sys_instance = None
|
||||
phc2sys_config = None
|
||||
phc_interface = None
|
||||
ptp_device = None
|
||||
offset = None
|
||||
|
||||
def __init__(self, phc2sys_config, init=True):
|
||||
self._state = OsClockState()
|
||||
self.last_event_time = None
|
||||
self.phc2sys_instance = None
|
||||
self.phc_interface = None
|
||||
self.ptp_device = None
|
||||
self.offset = None
|
||||
self.phc2sys_config = phc2sys_config
|
||||
self.config = None
|
||||
self.phc2sys_ha_enabled = False
|
||||
self.phc2sys_com_socket = None
|
||||
|
||||
self.set_phc2sys_instance()
|
||||
|
||||
"""Normally initialize all fields, but allow these to be skipped to
|
||||
assist with unit testing or to short-circuit values if required.
|
||||
"""
|
||||
if init:
|
||||
self.get_os_clock_time_source()
|
||||
self.parse_phc2sys_config()
|
||||
if 'global' not in self.config.keys():
|
||||
self.phc2sys_ha_enabled = False
|
||||
elif 'ha_enabled' in self.config['global'].keys() \
|
||||
and self.config['global']['ha_enabled'] == '1':
|
||||
self.phc2sys_ha_enabled = True
|
||||
self.phc2sys_com_socket = self.config['global'].get('ha_phc2sys_com_socket', None)
|
||||
|
||||
if self.phc2sys_ha_enabled is True:
|
||||
self.set_phc2sys_ha_interface_and_phc
|
||||
else:
|
||||
self.get_os_clock_time_source()
|
||||
self.get_os_clock_offset()
|
||||
self.set_os_clock_state()
|
||||
|
||||
@ -46,6 +62,55 @@ class OsClockMonitor:
|
||||
LOG.debug("phc2sys config file: %s" % self.phc2sys_config)
|
||||
LOG.debug("phc2sys instance name: %s" % self.phc2sys_instance)
|
||||
|
||||
def parse_phc2sys_config(self):
|
||||
LOG.debug("Parsing %s" % self.phc2sys_config)
|
||||
config = configparser.ConfigParser(delimiters=' ')
|
||||
config.read(self.phc2sys_config)
|
||||
self.config = config
|
||||
|
||||
def query_phc2sys_socket(self, query, unix_socket=None):
|
||||
if unix_socket:
|
||||
try:
|
||||
client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
client_socket.connect(unix_socket)
|
||||
client_socket.send(query.encode())
|
||||
response = client_socket.recv(1024)
|
||||
response = response.decode()
|
||||
if response == "None":
|
||||
response = None
|
||||
return response
|
||||
except ConnectionRefusedError as err:
|
||||
LOG.error("Error connecting to phc2sys socket for instance %s: %s" % (
|
||||
self.phc2sys_instance, err))
|
||||
return None
|
||||
except FileNotFoundError as err:
|
||||
LOG.error("Error connecting to phc2sys socket for instance %s: %s" % (
|
||||
self.phc2sys_instance, err))
|
||||
return None
|
||||
finally:
|
||||
if hasattr(client_socket, 'close'):
|
||||
client_socket.close()
|
||||
else:
|
||||
LOG.warning("No socket path supplied for instance %s" % self.instance_name)
|
||||
return None
|
||||
|
||||
def set_phc2sys_ha_interface_and_phc(self):
|
||||
update_phc_interface = self.query_phc2sys_socket('clock source', self.phc2sys_com_socket)
|
||||
if update_phc_interface is None:
|
||||
LOG.info("No PHC device found for HA phc2sys, status is FREERUN.")
|
||||
self._state = OsClockState.Freerun
|
||||
self.phc_interface = update_phc_interface
|
||||
self.ptp_device = None
|
||||
elif update_phc_interface != self.phc_interface:
|
||||
LOG.info("Phc2sys source interface changed from %s to %s"
|
||||
% (self.phc_interface, update_phc_interface))
|
||||
self.phc_interface = update_phc_interface
|
||||
|
||||
if self.phc_interface is not None:
|
||||
self.ptp_device = self._get_interface_phc_device()
|
||||
|
||||
LOG.debug("Phc2sys HA interface: %s ptp_device: %s" % (self.phc_interface, self.ptp_device))
|
||||
|
||||
def get_os_clock_time_source(self, pidfile_path="/var/run/"):
|
||||
"""Determine which PHC is disciplining the OS clock"""
|
||||
self.phc_interface = None
|
||||
@ -104,7 +169,7 @@ class OsClockMonitor:
|
||||
if len(ptp_device) == 0:
|
||||
# Try the 0th interface instead, required for some NIC types
|
||||
phc_interface_base = self.phc_interface[:-1] + "0"
|
||||
LOG.error("No ptp device found at %s trying %s instead"
|
||||
LOG.info("No ptp device found at %s trying %s instead"
|
||||
% (pattern, phc_interface_base))
|
||||
pattern = "/hostsys/class/net/" + phc_interface_base + \
|
||||
"/device/ptp/*"
|
||||
@ -122,6 +187,11 @@ class OsClockMonitor:
|
||||
|
||||
def get_os_clock_offset(self):
|
||||
"""Get the os CLOCK_REALTIME offset"""
|
||||
|
||||
if self.phc2sys_ha_enabled is True:
|
||||
# Refresh the HA source interface before checking offset
|
||||
self.set_phc2sys_ha_interface_and_phc()
|
||||
|
||||
if self.ptp_device is None:
|
||||
# This may happen in virtualized environments
|
||||
LOG.warning("No PTP device. Defaulting offset value to 0.")
|
||||
|
@ -0,0 +1,10 @@
|
||||
[global]
|
||||
##
|
||||
## Default Data Set
|
||||
##
|
||||
domainNumber 24
|
||||
message_tag phc-inst1
|
||||
uds_address /var/run/ptp4l-ptp-inst1
|
||||
ha_enabled 1
|
||||
|
||||
[ens2f0]
|
@ -24,6 +24,16 @@ class OsClockMonitorTests(unittest.TestCase):
|
||||
self.clockmon.set_phc2sys_instance()
|
||||
assert self.clockmon.phc2sys_instance == "phc2sys-test"
|
||||
|
||||
def test_parse_phc2sys_config(self):
|
||||
self.clockmon = OsClockMonitor(phc2sys_config=phc2sys_test_config, init=False)
|
||||
self.clockmon.phc2sys_config = testpath + "test_input_files/phc2sys-test.conf"
|
||||
self.clockmon.parse_phc2sys_config()
|
||||
assert 'ha_enabled' not in self.clockmon.config['global'].keys()
|
||||
|
||||
self.clockmon.phc2sys_config = testpath + "test_input_files/phc2sys-ha-test.conf"
|
||||
self.clockmon.parse_phc2sys_config()
|
||||
assert 'ha_enabled' in self.clockmon.config['global'].keys()
|
||||
|
||||
def test_check_config_file_interface(self):
|
||||
self.clockmon = OsClockMonitor(phc2sys_config=phc2sys_test_config, init=False)
|
||||
self.clockmon.phc2sys_config = testpath + "test_input_files/phc2sys-test.conf"
|
||||
|
@ -12,8 +12,10 @@
|
||||
# Sync status provided as: 'Locked', 'Holdover', 'Freerun'
|
||||
#
|
||||
#
|
||||
import configparser
|
||||
import errno, os
|
||||
import os.path
|
||||
import socket
|
||||
import sys
|
||||
import subprocess
|
||||
import datetime
|
||||
@ -36,6 +38,9 @@ ptp_oper_dict = {
|
||||
|
||||
ptp4l_service_name = os.environ.get('PTP4L_SERVICE_NAME', 'ptp4l')
|
||||
phc2sys_service_name = os.environ.get('PHC2SYS_SERVICE_NAME', 'phc2sys')
|
||||
phc2sys_com_socket = os.environ.get('PHC2SYS_COM_SOCKET', "false")
|
||||
phc2sys_config_file_path = '%sphc2sys-%s.conf' % (constants.LINUXPTP_CONFIG_PATH,
|
||||
phc2sys_service_name)
|
||||
|
||||
# run subprocess and returns out, err, errcode
|
||||
def run_shell2(dir, ctx, args):
|
||||
@ -65,8 +70,37 @@ def check_critical_resources():
|
||||
phc2sys = True
|
||||
if os.path.isfile('%sptp4l-%s.conf' % (constants.LINUXPTP_CONFIG_PATH, ptp4l_service_name)):
|
||||
ptp4lconf = True
|
||||
if phc2sys_com_socket != "false":
|
||||
# User enabled phc2sys HA source clock validation
|
||||
phc2sys_source_clock = check_phc2sys_ha_source()
|
||||
if phc2sys_source_clock is None:
|
||||
phc2sys = False
|
||||
LOG.warning("No Phc2sys HA source clock found")
|
||||
return pmc, ptp4l, phc2sys, ptp4lconf
|
||||
|
||||
def check_phc2sys_ha_source():
|
||||
query = 'clock source'
|
||||
try:
|
||||
client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
client_socket.connect(phc2sys_com_socket)
|
||||
client_socket.send(query.encode())
|
||||
response = client_socket.recv(1024)
|
||||
response = response.decode()
|
||||
if response == "None":
|
||||
response = None
|
||||
return response
|
||||
except ConnectionRefusedError as err:
|
||||
LOG.error("Error connecting to phc2sys socket for instance %s: %s" % (
|
||||
phc2sys_service_name, err))
|
||||
return None
|
||||
except FileNotFoundError as err:
|
||||
LOG.error("Error connecting to phc2sys socket for instance %s: %s" % (
|
||||
phc2sys_service_name, err))
|
||||
return None
|
||||
finally:
|
||||
if hasattr(client_socket, 'close'):
|
||||
client_socket.close()
|
||||
|
||||
def check_results(result, total_ptp_keywords, port_count):
|
||||
# sync state is in 'Locked' state and will be overwritten if
|
||||
# it is not the case
|
||||
|
@ -219,6 +219,8 @@ spec:
|
||||
value: "{{ .Values.ptptracking.ptp4lServiceName }}"
|
||||
- name: PHC2SYS_SERVICE_NAME
|
||||
value: "{{ .Values.ptptracking.phc2sysServiceName }}"
|
||||
- name: PHC2SYS_COM_SOCKET
|
||||
value: "{{ .Values.ptptracking.phc2sysComSocket }}"
|
||||
- name: PYTHONPATH
|
||||
value: "/opt/ptptrackingfunction"
|
||||
- name: LOGGING_LEVEL
|
||||
|
@ -71,6 +71,7 @@ ptptracking:
|
||||
ptp4lSocket: /var/run/ptp4l-ptp4l-legacy
|
||||
ptp4lServiceName: ptp4l-legacy
|
||||
phc2sysServiceName: phc2sys-legacy
|
||||
phc2sysComSocket: False
|
||||
logging_level: INFO
|
||||
image:
|
||||
repository: starlingx/notificationservice-base
|
||||
|
Loading…
x
Reference in New Issue
Block a user