![Cole Walker](/assets/img/avatar_default.png)
Add the required code and Dockerfile to build a Debian based container image of notificationservice-base to support v1 API backwards compatibility. The notificationservice-base-v1 code added here is being ported forward from stx.6.0. This code existed as-is in StarlingX prior to the work done to implement the ORAN Notification API. It is being returned in order to support user's needs for backwards compatbility. Subsequent work will done to update the helm charts, allowing notificationservice-base-v1-api and notificationservice-base-v2-api (ORAN Notification) to be deployed in parallel. Test plan: PASS: Build container image PASS: Manually deploy image and test with v1 client Story: 2010538 Task: 47175 Change-Id: I2f672fe1b226cb60b62bd5fe7f8abbedc7b0c4cc
199 lines
7.3 KiB
Python
199 lines
7.3 KiB
Python
#! /usr/bin/python3
|
|
#
|
|
# Copyright (c) 2021-2023 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
#
|
|
# 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 logging
|
|
from trackingfunctionsdk.common.helpers import constants
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
# dictionary includes PMC commands used and keywords of intrest
|
|
ptp_oper_dict = {
|
|
#[pmc cmd, ptp 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, constants.GRANDMASTER_IDENTITY],
|
|
4: ["'GET TIME_PROPERTIES_DATA_SET'", constants.TIME_TRACEABLE],
|
|
5: ["'GET DEFAULT_DATA_SET'", constants.CLOCK_IDENTITY]
|
|
}
|
|
|
|
ptp4l_service_name = os.environ.get('PTP4L_SERVICE_NAME', 'ptp4l')
|
|
phc2sys_service_name = os.environ.get('PHC2SYS_SERVICE_NAME', 'phc2sys')
|
|
|
|
# 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-%s.pid' % ptp4l_service_name):
|
|
ptp4l = True
|
|
if os.path.isfile('/var/run/phc2sys-%s.pid' % phc2sys_service_name):
|
|
phc2sys = True
|
|
if os.path.isfile('%sptp4l-%s.conf' % (constants.LINUXPTP_CONFIG_PATH, ptp4l_service_name)):
|
|
ptp4lconf = True
|
|
return pmc, ptp4l, phc2sys, ptp4lconf
|
|
|
|
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
|
|
sync_state = constants.LOCKED_PHC_STATE
|
|
|
|
local_gm = False
|
|
|
|
# check for a healthy result
|
|
if len(result) != total_ptp_keywords:
|
|
sync_state = constants.FREERUN_PHC_STATE
|
|
LOG.warning('results are not complete, returning FREERUN')
|
|
return sync_state
|
|
# determine the current sync state
|
|
if (result[constants.GM_PRESENT].lower() != constants.GM_IS_PRESENT
|
|
and result[constants.GRANDMASTER_IDENTITY] != result[constants.CLOCK_IDENTITY]):
|
|
sync_state = constants.FREERUN_PHC_STATE
|
|
elif result[constants.GRANDMASTER_IDENTITY] == result[constants.CLOCK_IDENTITY]:
|
|
local_gm = True
|
|
LOG.debug("Local node is a GM")
|
|
# else:
|
|
# local_gm = True
|
|
for port in range(1, port_count + 1):
|
|
if result[constants.PORT.format(port)].lower() == constants.SLAVE_MODE:
|
|
break
|
|
elif local_gm and result[constants.PORT.format(port)].lower() == constants.MASTER_MODE:
|
|
break
|
|
else:
|
|
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_VALUE6]):
|
|
sync_state = constants.FREERUN_PHC_STATE
|
|
return sync_state
|
|
|
|
def ptpsync():
|
|
result = {}
|
|
total_ptp_keywords = 0
|
|
port_count = 0
|
|
|
|
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 " + constants.LINUXPTP_CONFIG_PATH + "ptp4l-" + ptp4l_service_name + ".conf " + cmd
|
|
|
|
ptp_keyword = ptp_dict_to_use[key][1:]
|
|
total_ptp_keywords += len(ptp_keyword)
|
|
|
|
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 ptp_keyword:
|
|
if state[0] == item:
|
|
if item == constants.PORT_STATE:
|
|
port_count += 1
|
|
result.update({constants.PORT.format(port_count):state[1]})
|
|
else:
|
|
state[1] = state[1].replace('\\n','')
|
|
state[1] = state[1].replace('\'','')
|
|
result.update({state[0]:state[1]})
|
|
# making sure at least one port is available
|
|
if port_count == 0:
|
|
port_count = 1
|
|
# adding the possible ports minus one keyword not used, "portState"
|
|
total_ptp_keywords = total_ptp_keywords + port_count - 1
|
|
return result, total_ptp_keywords, port_count
|
|
|
|
def ptp_status(holdover_time, freq, 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 = round(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, total_ptp_keywords, port_count = ptpsync()
|
|
sync_state = check_results(result, total_ptp_keywords, port_count)
|
|
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
|