config/controllerconfig/controllerconfig/upgrade-scripts/61-move-ptp-parameters.py

435 lines
18 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright (c) 2022 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# This script moves legacy PTP configuration (contents) from other tables:
# - Global (system-wide) ptp4l configuration in 'ptp' table, by creating
# a "legacy" 'ptp4l' entry in 'ptp_instances' table and inserting the
# corresponding entries in 'ptp_parameters';
# - If advanced (specialized) ptp4l configuration is found in
# 'service_parameter' table, it inserts the corresponding entry(ies) in
# 'ptp_parameters' and refers to the "legacy" 'ptp4l' instance created
# for global (system-wide) configuration;
# - If phc2sys configuration is found in 'service_parameter' table, it
# inserts a 'phc2sys' entry in 'ptp_instances' table and inserts the
# corresponding entry(ies) in 'ptp_parameters';
# - If any interface has 'ptp_role' not equal to 'none', it inserts a
# 'ptp4l' entry in 'ptp_instances' and inserts the corresponding entry
# in 'ptp_parameters'.
import os
import sys
import psycopg2
from controllerconfig.common import log
from datetime import datetime
from oslo_utils import uuidutils
from psycopg2.extras import DictCursor
LOG = log.get_logger(__name__)
INTERFACE_PTP_ROLE_NONE = 'none'
PLATFORM_PATH = '/opt/platform' # following tsconfig
# Hidden file indicating the update/upgrade from legacy configuration
# has been already run
PTP_UPDATE_PARAMETERS_DONE = '.update_ptp_parameters_done'
# PTP instance types
PTP_INSTANCE_TYPE_PTP4L = 'ptp4l'
PTP_INSTANCE_TYPE_PHC2SYS = 'phc2sys'
# PTP instances created during migration
PTP_INSTANCE_LEGACY_PTP4L = 'ptp4l-legacy'
PTP_INSTANCE_LEGACY_PHC2SYS = 'phc2sys-legacy'
# PTP interfaces created during migration
PTP_INTERFACE_LEGACY_PTP4L = 'ptp4lif-legacy'
PTP_INTERFACE_LEGACY_PHC2SYS = 'phc2sysif-legacy'
# PTP parameter: owner types
PTP_PARAMETER_OWNER_INSTANCE = 'ptp-instance'
PTP_PARAMETER_OWNER_INTERFACE = 'ptp-interface'
# Global PTP configuration migrated to legacy instance
PTP_PARAMETER_DELAY_MECHANISM = 'delay_mechanism'
PTP_PARAMETER_TIME_STAMPING = 'time_stamping'
PTP_PARAMETER_NETWORK_TRANSPORT = 'network_transport'
# PTP service parameter sections
SERVICE_PARAM_SECTION_PTP_GLOBAL = 'global'
SERVICE_PARAM_SECTION_PTP_PHC2SYS = 'phc2sys'
# Special PTP service parameters migrated from legacy configuration
PTP_PARAMETER_UDS_ADDRESS = 'uds_address'
PTP_PARAMETER_DOMAIN_NUMBER = 'domainNumber'
PTP_PARAMETER_DEFAULT_DOMAIN = '0'
PTP_PARAMETER_BC_JBOD = 'boundary_clock_jbod'
PTP_BOUNDARY_CLOCK_JBOD_1 = '1'
# PTP service parameters NOT migrated from legacy configuration
PTP_PARAMETER_UPDATE_RATE = 'update-rate'
PTP_PARAMETER_SUMMARY_UPDATES = 'summary-updates'
def main():
action = None
from_release = None
to_release = None
arg = 1
while arg < len(sys.argv):
if arg == 1:
from_release = sys.argv[arg]
elif arg == 2:
to_release = sys.argv[arg]
elif arg == 3:
action = sys.argv[arg]
else:
print("Invalid option %s." % sys.argv[arg])
return 1
arg += 1
log.configure()
LOG.info("%s invoked from_release = %s to_release = %s action = %s"
% (sys.argv[0], from_release, to_release, action))
res = 0
if action == "migrate" and (
from_release == '21.05' or from_release == '21.12'):
# Avoid attempt of updating PTP configuration after this upgrade
TO_CONFIG_PATH = PLATFORM_PATH + '/config/' + to_release + '/'
to_file = os.path.join(TO_CONFIG_PATH, PTP_UPDATE_PARAMETERS_DONE)
open(to_file, 'w').close()
# First check on filesystem to detect if some update from old PTP
# configuration has been already done (before upgrading)
FROM_CONFIG_PATH = PLATFORM_PATH + '/config/' + from_release + '/'
from_file = os.path.join(FROM_CONFIG_PATH, PTP_UPDATE_PARAMETERS_DONE)
if not os.path.isfile(from_file):
conn = psycopg2.connect("dbname=sysinv user=postgres")
try:
# Second check, using the restored database contents
if _legacy_instances_not_found(conn):
_move_ptp_parameters(conn)
except psycopg2.Error as ex:
LOG.exception(ex)
res = 1
except Exception as ex:
LOG.exception(ex)
res = 1
finally:
if to_release.startswith('22'):
_cleanup_ptp_service_parameters(conn)
# Committing all changes
LOG.info("Committing all PTP-related changes into database")
conn.commit()
conn.close()
return res
def _legacy_instances_not_found(connection):
with connection.cursor(cursor_factory=DictCursor) as cur:
cur.execute("SELECT id FROM ptp_instances WHERE "
"name = %s OR name = %s;",
(PTP_INSTANCE_LEGACY_PTP4L,
PTP_INSTANCE_LEGACY_PHC2SYS))
instance = cur.fetchone()
if instance is not None:
LOG.info("PTP legacy instance found with id = %s" % instance['id'])
return False
LOG.info("No PTP legacy instances found")
# If no legacy instances are present BUT any other instance is found,
# it would mean that instance must have been created after patching the
# current release making the new API fully functional, while legacy API was
# deprecated since then.
# In such case, legacy instances shouldn't be created automatically, even
# even based on the still existing (but now legacy and potentially
# untouched) 'ptp' table, because that would be an undesired behavior
with connection.cursor(cursor_factory=DictCursor) as cur:
cur.execute("SELECT id FROM ptp_instances;")
instance = cur.fetchone()
if instance is not None:
LOG.info("PTP legacy instances dismissed")
return False
return True
def _insert_ptp_parameter_owner(connection, owner_type, capabilities=None):
owner_uuid = uuidutils.generate_uuid()
with connection.cursor(cursor_factory=DictCursor) as cur:
LOG.debug("Creating PTP parameter owner %s" % owner_uuid)
cur.execute("INSERT INTO ptp_parameter_owners "
"(created_at, uuid, type, capabilities)"
"VALUES (%s, %s, %s, %s);",
(datetime.now(), owner_uuid, owner_type, capabilities))
cur.execute("SELECT id FROM ptp_parameter_owners WHERE uuid = %s;",
(owner_uuid,))
row = cur.fetchone()
owner_id = row['id']
return (owner_id, owner_uuid)
def _insert_ptp_instance(connection,
id,
name,
service,
extra_info=None):
with connection.cursor(cursor_factory=DictCursor) as cur:
LOG.debug("Creating PTP instance %s id %d" % (name, id))
cur.execute("INSERT INTO ptp_instances "
"(id, name, service, extra_info) "
"VALUES (%s, %s, %s, %s);",
(id, name, service, extra_info))
def _insert_ptp_interface(connection,
id,
name,
instance_id,
extra_info=None):
with connection.cursor(cursor_factory=DictCursor) as cur:
LOG.debug("Creating PTP interface %s id %d" % (name, id))
cur.execute("INSERT INTO ptp_interfaces "
"(id, name, ptp_instance_id, extra_info) "
"VALUES (%s, %s, %s, %s);",
(id, name, instance_id, extra_info))
def _insert_ptp_parameter(connection, name, value):
param_uuid = uuidutils.generate_uuid()
with connection.cursor(cursor_factory=DictCursor) as cur:
LOG.debug("Creating PTP parameter %s=%s" % (name, value))
cur.execute("INSERT INTO ptp_parameters "
"(created_at, uuid, name, value) "
"VALUES (%s, %s, %s, %s);",
(datetime.now(), param_uuid, name, value))
return param_uuid
def _add_parameter_to_instance(connection, owner_uuid, param_uuid):
with connection.cursor(cursor_factory=DictCursor) as cur:
LOG.debug("Adding PTP parameter %s to %s" % (param_uuid, owner_uuid))
cur.execute("INSERT INTO ptp_parameter_ownerships "
"(created_at, uuid, parameter_uuid, owner_uuid) "
"VALUES (%s, %s, %s, %s);",
(datetime.now(), uuidutils.generate_uuid(), param_uuid,
owner_uuid))
def _assign_instance_to_host(connection, instance_id, host_id):
with connection.cursor(cursor_factory=DictCursor) as cur:
LOG.debug("Assigning PTP instance %d to host %d" %
(instance_id, host_id))
cur.execute("INSERT INTO ptp_instance_maps "
"(created_at, uuid, host_id, ptp_instance_id) "
"VALUES (%s, %s, %s, %s);",
(datetime.now(), uuidutils.generate_uuid(), host_id,
instance_id))
def _assign_ptp_to_interface(connection, ptp_interface_id, interface_id):
with connection.cursor(cursor_factory=DictCursor) as cur:
LOG.debug("Assigning PTP interface %d to interface %d" %
(ptp_interface_id, interface_id))
cur.execute("INSERT INTO ptp_interface_maps "
"(created_at, uuid, interface_id, ptp_interface_id) "
"VALUES (%s, %s, %s, %s);",
(datetime.now(), uuidutils.generate_uuid(), interface_id,
ptp_interface_id))
def _cleanup_ptp_service_parameters(connection):
with connection.cursor(cursor_factory=DictCursor) as cur:
LOG.info("Removing PTP configuration from 'service_parameter' table")
cur.execute("DELETE FROM service_parameter WHERE service = 'ptp';")
def _move_ptp_parameters(connection):
with connection.cursor(cursor_factory=DictCursor) as cur:
# List all the hosts with clock_synchronization=ptp
cur.execute("SELECT id FROM i_host "
"WHERE clock_synchronization = 'ptp';")
ptp_hosts = cur.fetchall()
LOG.debug("There are %d hosts with clock_synchronization=ptp" %
len(ptp_hosts))
with connection.cursor(cursor_factory=DictCursor) as cur:
# List all PTP parameters in service-parameters table
cur.execute("SELECT section, name, value FROM service_parameter "
"WHERE service = 'ptp';")
param_entries = cur.fetchall()
LOG.debug("There are %d PTP rows in 'service_parameter' table" %
len(param_entries))
if len(ptp_hosts) == 0 and len(param_entries) == 0:
# No need for upgrade
return
with connection.cursor(cursor_factory=DictCursor) as cur:
# List all the interfaces with ptp_role!=none
cur.execute("SELECT id FROM interfaces WHERE ptp_role <> %s;",
(INTERFACE_PTP_ROLE_NONE,))
ptp_ifaces = cur.fetchall()
LOG.debug("There are %d interfaces with ptp_role != none" %
len(ptp_ifaces))
LOG.info("Creating PTP instances for legacy parameters")
# Take system-wide parameters from legacy configuration
with connection.cursor(cursor_factory=DictCursor) as cur:
cur.execute("SELECT mechanism, mode, transport FROM ptp;")
ptp_config = cur.fetchone()
delay_mechanism = str(ptp_config['mechanism']).upper()
time_stamping = str(ptp_config['mode']).lower()
network_transport = str(ptp_config['transport']).upper()
# Legacy instance for system-wide parameters and those of
# section "global" in service-parameters table
(ptp4l_id, ptp4l_uuid) = _insert_ptp_parameter_owner(
connection, PTP_PARAMETER_OWNER_INSTANCE)
_insert_ptp_instance(connection,
ptp4l_id,
PTP_INSTANCE_LEGACY_PTP4L,
PTP_INSTANCE_TYPE_PTP4L)
# Legacy PTP interface associated to legacy ptp4l instance
(ptp4lif_id, ptp4lif_uuid) = _insert_ptp_parameter_owner(
connection, PTP_PARAMETER_OWNER_INTERFACE)
_insert_ptp_interface(connection,
ptp4lif_id,
PTP_INTERFACE_LEGACY_PTP4L,
ptp4l_id)
# Legacy instance for parameters of section "phc2sys"
(phc2sys_id, phc2sys_uuid) = _insert_ptp_parameter_owner(
connection, PTP_PARAMETER_OWNER_INSTANCE)
_insert_ptp_instance(connection,
phc2sys_id,
PTP_INSTANCE_LEGACY_PHC2SYS,
PTP_INSTANCE_TYPE_PHC2SYS)
# Legacy PTP interface associated to legacy phc2sys instance
(phc2sysif_id, phc2sysif_uuid) = _insert_ptp_parameter_owner(
connection, PTP_PARAMETER_OWNER_INTERFACE)
_insert_ptp_interface(connection,
phc2sysif_id,
PTP_INTERFACE_LEGACY_PHC2SYS,
phc2sys_id)
# Add 'uds_address' parameter to phy2sys instance for linkage
# with ptp4l instance
uds_address_path = '/var/run/ptp4l-%s' % PTP_INSTANCE_LEGACY_PTP4L
uds_address_uuid = _insert_ptp_parameter(connection,
PTP_PARAMETER_UDS_ADDRESS,
uds_address_path)
_add_parameter_to_instance(connection, phc2sys_uuid, uds_address_uuid)
# Assign legacy instances to all hosts with clock_synchronization=ptp
for host in ptp_hosts:
_assign_instance_to_host(connection, ptp4l_id, host['id'])
_assign_instance_to_host(connection, phc2sys_id, host['id'])
# Assign legacy PTP interfaces to all interfaces with ptp_role!=none
for iface in ptp_ifaces:
_assign_ptp_to_interface(connection, ptp4lif_id, iface['id'])
_assign_ptp_to_interface(connection, phc2sysif_id, iface['id'])
# Copy service-parameter PTP entries, if any
domain_number = PTP_PARAMETER_DEFAULT_DOMAIN
for param in param_entries:
if (param['name'] == PTP_PARAMETER_UPDATE_RATE or
param['name'] == PTP_PARAMETER_SUMMARY_UPDATES):
LOG.info("Found %s parameter, ignored" % param['name'])
continue
if param['name'] == PTP_PARAMETER_DOMAIN_NUMBER:
domain_number = param['value'] # overwrite default
continue # skip it for below
if param['name'] == PTP_PARAMETER_DELAY_MECHANISM:
delay_mechanism = str(param['value']).upper() # overwrite global
continue # skip it for below
if param['name'] == PTP_PARAMETER_TIME_STAMPING:
time_stamping = str(param['value']).lower() # overwrite global
continue # skip it for below
if param['name'] == PTP_PARAMETER_NETWORK_TRANSPORT:
network_transport = str(param['value']).upper() # overwrite global
continue # skip it for below
if param['section'] == SERVICE_PARAM_SECTION_PTP_GLOBAL:
owner_uuid = ptp4l_uuid
elif param['section'] == SERVICE_PARAM_SECTION_PTP_PHC2SYS:
owner_uuid = phc2sys_uuid
else:
raise Exception("Unexpected PTP section in "
"'service-parameter' table")
param_uuid = _insert_ptp_parameter(connection,
param['name'],
param['value'])
_add_parameter_to_instance(connection, owner_uuid, param_uuid)
# Whatever 'global' parameter has been found, it must be
# added also to phc2sys instance, since now this has own
# configuration file
if param['section'] == SERVICE_PARAM_SECTION_PTP_GLOBAL:
_add_parameter_to_instance(connection,
phc2sys_uuid,
param_uuid)
domain_number_uuid = _insert_ptp_parameter(
connection, PTP_PARAMETER_DOMAIN_NUMBER, domain_number)
_add_parameter_to_instance(connection, ptp4l_uuid, domain_number_uuid)
_add_parameter_to_instance(connection,
phc2sys_uuid,
domain_number_uuid)
ptp_delay_mechanism_uuid = _insert_ptp_parameter(
connection, PTP_PARAMETER_DELAY_MECHANISM, delay_mechanism)
_add_parameter_to_instance(connection,
ptp4l_uuid,
ptp_delay_mechanism_uuid)
_add_parameter_to_instance(connection,
phc2sys_uuid,
ptp_delay_mechanism_uuid)
ptp_time_stamping_uuid = _insert_ptp_parameter(
connection, PTP_PARAMETER_TIME_STAMPING, time_stamping)
_add_parameter_to_instance(connection,
ptp4l_uuid,
ptp_time_stamping_uuid)
_add_parameter_to_instance(connection,
phc2sys_uuid,
ptp_time_stamping_uuid)
ptp_network_transport_uuid = _insert_ptp_parameter(
connection, PTP_PARAMETER_NETWORK_TRANSPORT, network_transport)
_add_parameter_to_instance(connection,
ptp4l_uuid,
ptp_network_transport_uuid)
_add_parameter_to_instance(connection,
phc2sys_uuid,
ptp_network_transport_uuid)
# Add 'boundary_clock_jbod' parameter to ptp4l instance if mode is
# "hardware"
if time_stamping == 'hardware':
bc_clock_jbod_uuid = _insert_ptp_parameter(
connection, PTP_PARAMETER_BC_JBOD, PTP_BOUNDARY_CLOCK_JBOD_1)
_add_parameter_to_instance(connection, ptp4l_uuid, bc_clock_jbod_uuid)
if __name__ == "__main__":
sys.exit(main())