Add ptpsync implementation

Story: 2008529
Task: 41719

Signed-off-by: Udi Schwager <Udi.Schwager@windriver.com>
Change-Id: I9c6f38dd19cab5de75d55c0d061f02fb29669ee4
This commit is contained in:
Bin Yang 2021-01-30 09:16:26 +08:00 committed by Udi Schwager
parent 545e6b6bb0
commit 48dc8e1ca4
2 changed files with 173 additions and 2 deletions

View File

@ -0,0 +1,19 @@
# phc states constants
FREERUN_PHC_STATE = "Freerun"
LOCKED_PHC_STATE = "Locked"
HOLDOVER_PHC_STATE = "Holdover"
UNKNOWN_PHC_STATE = "Unknown"
# PMC command constants
PORT_STATE = "portState"
GM_PRESENT = "gmPresent"
MASTER_OFFSET = "master_offset"
GM_CLOCK_CLASS = "gm.ClockClass"
TIME_TRACEABLE = "timeTraceable"
# expected values for valid ptp state
SLAVE_MODE = "slave"
TIME_IS_TRACEABLE1 = "1"
TIME_IS_TRACEABLE2 = "true"
GM_IS_PRESENT = "true"
CLOCK_CLASS_VALUE1 = "6"
CLOCK_CLASS_VALUE2 = "7"
CLOCK_CLASS_VALUE3 = "135"

View File

@ -1,5 +1,157 @@
#! /usr/bin/python3
#
# This script provides the PTP synchronization status
# for PTP NIC configured as subordinate (slave mode)
# It relies on Linux ptp4l (PMC) module in order to work
# Sync status provided as: 'Locked', 'Holdover', 'Freerun'
#
#
import errno, os
import os.path
import sys
import subprocess
import datetime
import constants
import logging
LOG = logging.getLogger(__name__)
# dictionary includes PMC commands used and keywords of intrest
ptp_oper_dict = {
#[cmd, anchor keywords,...]
1: ["'GET PORT_DATA_SET'", constants.PORT_STATE],
2: ["'GET TIME_STATUS_NP'", constants.GM_PRESENT, constants.MASTER_OFFSET],
3: ["'GET PARENT_DATA_SET'", constants.GM_CLOCK_CLASS],
4: ["'GET TIME_PROPERTIES_DATA_SET'", constants.TIME_TRACEABLE]
}
# run subprocess and returns out, err, errcode
def run_shell2(dir, ctx, args):
cwd = os.getcwd()
os.chdir(dir)
process = subprocess.Popen(args, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = process.communicate()
errcode = process.returncode
os.chdir(cwd)
return out, err, errcode
def check_critical_resources():
pmc = False
ptp4l = False
phc2sys = False
ptp4lconf = False
if os.path.isfile('/usr/sbin/pmc'):
pmc = True
if os.path.isfile('/var/run/ptp4l.pid'):
ptp4l = True
if os.path.isfile('/var/run/phc2sys.pid'):
phc2sys = True
if os.path.isfile('/ptp/ptp4l.conf'):
ptp4lconf = True
return pmc, ptp4l, phc2sys, ptp4lconf
def check_results(result):
# sync state is in 'Locked' state and will be overwritten if
# it is not the case
sync_state = constants.LOCKED_PHC_STATE
# determine the current sync state
if result[constants.GM_PRESENT].lower() != constants.GM_IS_PRESENT:
sync_state = constants.FREERUN_PHC_STATE
if result[constants.PORT_STATE].lower() != constants.SLAVE_MODE:
sync_state = constants.FREERUN_PHC_STATE
if 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_VALUE1, constants.CLOCK_CLASS_VALUE2, constants.CLOCK_CLASS_VALUE3]:
sync_state = constants.FREERUN_PHC_STATE
return sync_state
def ptpsync():
result = {}
ptp_dict_to_use = ptp_oper_dict
len_dic = len(ptp_dict_to_use)
for key in range(1,len_dic+1):
cmd = ptp_dict_to_use[key][0]
cmd = "pmc -b 0 -u -f /ptp/ptp4l.conf " + cmd
search_word = ptp_dict_to_use[key][1:]
out, err, errcode = run_shell2('.', None, cmd)
if errcode != 0:
LOG.warning('pmc command returned unknown result')
sys.exit(0)
out = str(out)
try:
out = out.split("\\n\\t\\t")
except:
LOG.warning('cannot split "out" into a list')
sys.exit(0)
for state in out:
try:
state = state.split()
except:
LOG.warning('cannot split "state" into a list')
sys.exit(0)
if len(state) <= 1:
LOG.warning('not received the expected list length')
sys.exit(0)
for item in search_word:
if state[0] == item:
result.update({state[0]:state[1]})
return result
'''ptp_status API stub'''
def ptp_status(holdover_time, freq, sync_state, event_time):
return new_event, sync_state, event_time
result = {}
# holdover_time - time phc can maintain clock
# freq - the frequently for monitoring the ptp status
# sync_state - the current ptp state
# event_time - the last time that ptp status was changed
####################################
# event states: #
# Locked —> Holdover —> Freerun #
# Holdover —> Locked #
# Freerun —> Locked #
####################################
current_time = datetime.datetime.now().timestamp()
time_in_holdover = current_time - event_time
previous_sync_state = sync_state
# max holdover time is calculated to be in a 'safety' zoon
max_holdover_time = (holdover_time - freq * 2)
pmc, ptp4l, phc2sys, ptp4lconf = check_critical_resources()
# run pmc command if preconditions met
if pmc and ptp4l and phc2sys and ptp4lconf:
result = ptpsync()
sync_state = check_results(result)
else:
sync_state = constants.FREERUN_PHC_STATE
# determine if transition into holdover mode (cannot be in holdover if system clock is not in sync)
if sync_state == constants.FREERUN_PHC_STATE and phc2sys:
if previous_sync_state in [constants.UNKNOWN_PHC_STATE, constants.FREERUN_PHC_STATE]:
sync_state = constants.FREERUN_PHC_STATE
elif previous_sync_state == constants.LOCKED_PHC_STATE:
sync_state = constants.HOLDOVER_PHC_STATE
elif previous_sync_state == constants.HOLDOVER_PHC_STATE and time_in_holdover < max_holdover_time:
sync_state = constants.HOLDOVER_PHC_STATE
else:
sync_state == constants.FREERUN_PHC_STATE
# determine if ptp sync state has changed since the last one
if sync_state != previous_sync_state:
new_event = "true"
event_time = datetime.datetime.now().timestamp()
else:
new_event = "false"
return new_event, sync_state, event_time