Merge "DEV: Extend sysinv API to support PTP monitoring configuration"
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2013-2023 Wind River Systems, Inc.
|
||||
# Copyright (c) 2013-2023, 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
@@ -113,6 +113,13 @@ SB_SUPPORTED_NETWORKS = {
|
||||
SB_TYPE_CEPH: [NETWORK_TYPE_MGMT, NETWORK_TYPE_CLUSTER_HOST]
|
||||
}
|
||||
|
||||
# PTP definitions
|
||||
PTP_INSTANCE_TYPE_MONITORING = "monitoring"
|
||||
PTP_INSTANCE_TYPE_MONITORING_SUPPORTED_PARAMETERS = [
|
||||
"devices", "satellite_count", "signal_quality_db",
|
||||
"cmdline_opts"
|
||||
]
|
||||
|
||||
EXPIRED = "--expired"
|
||||
SOON_TO_EXPIRY = "--soon_to_expiry"
|
||||
VALIDITY = "Validity"
|
||||
|
@@ -1,11 +1,12 @@
|
||||
########################################################################
|
||||
#
|
||||
# Copyright (c) 2021-2023 Wind River Systems, Inc.
|
||||
# Copyright (c) 2021-2023, 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
########################################################################
|
||||
|
||||
from cgtsclient.common import constants
|
||||
from cgtsclient.common import utils
|
||||
from cgtsclient import exc
|
||||
from cgtsclient.v1 import ihost as ihost_utils
|
||||
@@ -45,7 +46,10 @@ def do_ptp_instance_list(cc, args):
|
||||
help="Name of PTP instance [REQUIRED]")
|
||||
@utils.arg('service',
|
||||
metavar='<service type>',
|
||||
choices=['ptp4l', 'phc2sys', 'ts2phc', 'clock', 'synce4l'],
|
||||
choices=[
|
||||
'ptp4l', 'phc2sys', 'ts2phc', 'clock', 'synce4l',
|
||||
constants.PTP_INSTANCE_TYPE_MONITORING
|
||||
],
|
||||
help="Service type [REQUIRED]")
|
||||
def do_ptp_instance_add(cc, args):
|
||||
"""Add a PTP instance."""
|
||||
@@ -81,6 +85,20 @@ def _ptp_instance_parameter_op(cc, op, instance, parameters):
|
||||
if len(parameters) == 0:
|
||||
raise exc.CommandError('Missing PTP parameter')
|
||||
ptp_instance = ptp_instance_utils._find_ptp_instance(cc, instance)
|
||||
|
||||
# check for supported parameters in case of monitoring type
|
||||
if (ptp_instance.service == constants.PTP_INSTANCE_TYPE_MONITORING and op == "add"):
|
||||
for param_keypair in parameters:
|
||||
if param_keypair.find("=") < 0:
|
||||
raise exc.CommandError(f"Bad PTP parameter keypair: {param_keypair}")
|
||||
(param_name, param_value) = param_keypair.split("=", 1)
|
||||
|
||||
if param_name not in constants.PTP_INSTANCE_TYPE_MONITORING_SUPPORTED_PARAMETERS:
|
||||
raise exc.CommandError(
|
||||
f"Parameter {param_name} is not supported. Supported parameters:"
|
||||
f"{constants.PTP_INSTANCE_TYPE_MONITORING_SUPPORTED_PARAMETERS}"
|
||||
)
|
||||
|
||||
patch = []
|
||||
for parameter in parameters:
|
||||
patch.append({'op': op,
|
||||
|
@@ -1,11 +1,12 @@
|
||||
########################################################################
|
||||
#
|
||||
# Copyright (c) 2021 Wind River Systems, Inc.
|
||||
# Copyright (c) 2021, 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
########################################################################
|
||||
|
||||
from cgtsclient.common import constants
|
||||
from cgtsclient.common import utils
|
||||
from cgtsclient import exc
|
||||
from cgtsclient.v1 import ihost as ihost_utils
|
||||
@@ -60,6 +61,13 @@ def do_ptp_interface_add(cc, args):
|
||||
# Check the PTP instance exists
|
||||
ptp_instance = ptp_instance_utils._find_ptp_instance(
|
||||
cc, args.ptpinstancenameorid)
|
||||
|
||||
# Do not allow for monitoring service type.
|
||||
if ptp_instance.service == constants.PTP_INSTANCE_TYPE_MONITORING:
|
||||
raise exc.CommandError(
|
||||
"PTP instance of monitoring type does not support interface"
|
||||
)
|
||||
|
||||
data.update({'ptp_instance_uuid': ptp_instance.uuid})
|
||||
|
||||
ptp_interface = cc.ptp_interface.create(**data)
|
||||
|
@@ -2036,6 +2036,15 @@ class HostController(rest.RestController):
|
||||
constants.NETWORK_TYPE_MGMT, True)
|
||||
return address.address
|
||||
|
||||
def _exists_monitoring_instance_on_host(self, host_uuid):
|
||||
ptp_instances = pecan.request.dbapi.ptp_instances_get_list(host_uuid)
|
||||
|
||||
for instance in ptp_instances:
|
||||
if instance["service"] == constants.PTP_INSTANCE_TYPE_MONITORING:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _patch(self, uuid, patch):
|
||||
log_start = cutils.timestamped("ihost_patch_start")
|
||||
|
||||
@@ -2048,12 +2057,22 @@ class HostController(rest.RestController):
|
||||
ptp_instance_id = p.get('value')
|
||||
try:
|
||||
# Check PTP instance exists
|
||||
pecan.request.dbapi.ptp_instance_get(ptp_instance_id)
|
||||
ptp_instance = pecan.request.dbapi.ptp_instance_get(ptp_instance_id)
|
||||
except exception.PtpInstanceNotFound:
|
||||
raise wsme.exc.ClientSideError(_("No PTP instance object"))
|
||||
values = {'host_id': ihost_obj.id,
|
||||
'ptp_instance_id': ptp_instance_id}
|
||||
if p.get('op') == constants.PTP_PATCH_OPERATION_ADD:
|
||||
# Check constraint: single monitoring ptp instance on host
|
||||
if ptp_instance[
|
||||
"service"
|
||||
] == constants.PTP_INSTANCE_TYPE_MONITORING and self._exists_monitoring_instance_on_host(
|
||||
uuid
|
||||
):
|
||||
raise wsme.exc.ClientSideError(
|
||||
_("Monitoring ptp instance already exists on host")
|
||||
)
|
||||
|
||||
pecan.request.dbapi.ptp_instance_assign(values)
|
||||
else:
|
||||
pecan.request.dbapi.ptp_instance_remove(values)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 Wind River Systems, Inc.
|
||||
# Copyright (c) 2021-2023, 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
@@ -74,7 +74,8 @@ class PtpInstance(base.APIBase):
|
||||
constants.PTP_INSTANCE_TYPE_PHC2SYS,
|
||||
constants.PTP_INSTANCE_TYPE_TS2PHC,
|
||||
constants.PTP_INSTANCE_TYPE_CLOCK,
|
||||
constants.PTP_INSTANCE_TYPE_SYNCE4L)
|
||||
constants.PTP_INSTANCE_TYPE_SYNCE4L,
|
||||
constants.PTP_INSTANCE_TYPE_MONITORING)
|
||||
"Type of service of the PTP instance"
|
||||
|
||||
hostnames = types.MultiType([list])
|
||||
|
@@ -2403,6 +2403,10 @@ PTP_SYNCE_EEC_INVALID_VALUE = '0'
|
||||
PTP_SYNCE_TX_HEARTBEAT_MSEC = '1000'
|
||||
PTP_SYNCE_RX_HEARTBEAT_MSEC = '500'
|
||||
|
||||
# PTP instance monitoring default parameters
|
||||
PTP_MONITORING_SATELLITE_COUNT = "5"
|
||||
PTP_MONITORING_SIGNAL_QUALITY_DB_VALUE = "30"
|
||||
|
||||
# PTP pmc values
|
||||
PTP_PMC_CLOCK_CLASS = '248'
|
||||
PTP_PMC_CLOCK_ACCURACY = '0xfe'
|
||||
@@ -2422,6 +2426,7 @@ PTP_INSTANCE_TYPE_PHC2SYS = 'phc2sys'
|
||||
PTP_INSTANCE_TYPE_TS2PHC = 'ts2phc'
|
||||
PTP_INSTANCE_TYPE_CLOCK = 'clock'
|
||||
PTP_INSTANCE_TYPE_SYNCE4L = 'synce4l'
|
||||
PTP_INSTANCE_TYPE_MONITORING = "monitoring"
|
||||
|
||||
# PTP instances created during migration
|
||||
PTP_INSTANCE_LEGACY_PTP4L = 'ptp4l-legacy'
|
||||
|
@@ -323,8 +323,38 @@ class NetworkingPuppet(base.BasePuppet):
|
||||
|
||||
return config
|
||||
|
||||
def _set_ptp_instance_global_parameters(self, ptp_instances, ptp_parameters_instance):
|
||||
def _set_ptp_instance_monitoring_global_parameters(
|
||||
self, instance, ptp_parameters_instance
|
||||
):
|
||||
default_global_parameters = {
|
||||
"satellite_count": constants.PTP_MONITORING_SATELLITE_COUNT,
|
||||
"signal_quality_db": constants.PTP_MONITORING_SIGNAL_QUALITY_DB_VALUE,
|
||||
}
|
||||
default_cmdline_opts = ""
|
||||
|
||||
# Add default global parameters the instance
|
||||
instance["global_parameters"] = default_global_parameters
|
||||
instance["cmdline_opts"] = default_cmdline_opts
|
||||
|
||||
for global_param in ptp_parameters_instance:
|
||||
# Add the supplied instance parameters to global_parameters
|
||||
if instance["uuid"] in global_param["owners"]:
|
||||
instance["global_parameters"][global_param["name"]] = global_param[
|
||||
"value"
|
||||
]
|
||||
if "cmdline_opts" in instance["global_parameters"]:
|
||||
cmdline = instance["global_parameters"].pop("cmdline_opts")
|
||||
quotes = {"'", "\\'", '"', '\\"'}
|
||||
for quote in quotes:
|
||||
cmdline = cmdline.strip(quote)
|
||||
instance["cmdline_opts"] = cmdline
|
||||
|
||||
allowed_instance_fields = ["global_parameters", "cmdline_opts"]
|
||||
monitoring_config = {r: instance[r] for r in allowed_instance_fields}
|
||||
|
||||
return monitoring_config
|
||||
|
||||
def _set_ptp_instance_global_parameters(self, ptp_instances, ptp_parameters_instance):
|
||||
default_global_parameters = {
|
||||
# Default ptp4l parameters were determined during the original integration of PTP.
|
||||
# These defaults maintain the same PTP behaviour as single instance implementation.
|
||||
@@ -713,12 +743,20 @@ class NetworkingPuppet(base.BasePuppet):
|
||||
nic_clock_config = {}
|
||||
nic_clock_enabled = False
|
||||
ptp_instance_configs = []
|
||||
monitoring_instance_configs = []
|
||||
|
||||
for index, instance in enumerate(ptp_instances):
|
||||
if ptp_instances[index]['service'] == constants.PTP_INSTANCE_TYPE_CLOCK:
|
||||
clock_instance = ptp_instances[index]
|
||||
nic_clocks[instance['name']] = clock_instance.as_dict()
|
||||
nic_clocks[instance['name']]['interfaces'] = []
|
||||
elif (
|
||||
ptp_instances[index]["service"]
|
||||
== constants.PTP_INSTANCE_TYPE_MONITORING
|
||||
):
|
||||
ptp_instances[index][instance["name"]] = instance.as_dict()
|
||||
ptp_instances[index][instance["name"]]["interfaces"] = []
|
||||
monitoring_instance_configs.append(ptp_instances[index])
|
||||
else:
|
||||
ptp_instances[index][instance['name']] = instance.as_dict()
|
||||
ptp_instances[index][instance['name']]['interfaces'] = []
|
||||
@@ -751,14 +789,34 @@ class NetworkingPuppet(base.BasePuppet):
|
||||
else:
|
||||
ptp_config = {}
|
||||
|
||||
return {'platform::ptpinstance::config': ptp_config,
|
||||
'platform::ptpinstance::enabled': ptpinstance_enabled,
|
||||
'platform::ptpinstance::nic_clock::nic_clock_config': nic_clock_config,
|
||||
'platform::ptpinstance::nic_clock::nic_clock_enabled': nic_clock_enabled}
|
||||
# Generate the monitoring config
|
||||
monitoring_enabled = False
|
||||
monitoring_config = {}
|
||||
len_monitoring_instance_configs = len(monitoring_instance_configs)
|
||||
|
||||
if ptpinstance_enabled and len_monitoring_instance_configs > 0:
|
||||
# Only single monitoring instance per host is allowed.
|
||||
if len_monitoring_instance_configs == 1:
|
||||
monitoring_enabled = True
|
||||
monitoring_config = self._set_ptp_instance_monitoring_global_parameters(
|
||||
monitoring_instance_configs[0], ptp_parameters_instance
|
||||
)
|
||||
else:
|
||||
LOG.warning(
|
||||
f"PTP monitoring instances are {len_monitoring_instance_configs > 1} on host id {host.id}."
|
||||
)
|
||||
|
||||
return {
|
||||
'platform::ptpinstance::config': ptp_config,
|
||||
'platform::ptpinstance::enabled': ptpinstance_enabled,
|
||||
'platform::ptpinstance::monitoring::monitoring_config': monitoring_config,
|
||||
'platform::ptpinstance::monitoring::monitoring_enabled': monitoring_enabled,
|
||||
'platform::ptpinstance::nic_clock::nic_clock_config': nic_clock_config,
|
||||
'platform::ptpinstance::nic_clock::nic_clock_enabled': nic_clock_enabled,
|
||||
}
|
||||
|
||||
def _get_interface_config(self, networktype):
|
||||
config = {}
|
||||
|
||||
network_interface = interface.find_interface_by_type(
|
||||
self.context, networktype)
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2021-2022 Wind River Systems, Inc.
|
||||
# Copyright (c) 2021-2022, 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
@@ -91,6 +91,11 @@ class TestCreatePtpInstance(BasePtpInstanceTestCase):
|
||||
status_code=http_client.CONFLICT,
|
||||
error_message=error_message)
|
||||
|
||||
def test_create_ptp_instance_monitoring_ok(self):
|
||||
self._create_ptp_instance_success(
|
||||
"fake-instance-monitoring", constants.PTP_INSTANCE_TYPE_MONITORING
|
||||
)
|
||||
|
||||
|
||||
class TestHostPtpInstance(BasePtpInstanceTestCase):
|
||||
def setUp(self):
|
||||
@@ -140,6 +145,57 @@ class TestHostPtpInstance(BasePtpInstanceTestCase):
|
||||
self.assertEqual(response.content_type, 'application/json')
|
||||
self.assertEqual(response.status_code, http_client.OK)
|
||||
|
||||
def _assign_host_ptp_instance_monitoring_success(self):
|
||||
ptp_instance = dbutils.create_test_ptp_instance(
|
||||
name="test-instance", service=constants.PTP_INSTANCE_TYPE_MONITORING
|
||||
)
|
||||
ptp_instance_id = ptp_instance["id"]
|
||||
response = self.patch_json(
|
||||
self.get_host_url(self.controller.uuid),
|
||||
[
|
||||
{
|
||||
"path": constants.PTP_INSTANCE_ARRAY_PATH,
|
||||
"value": ptp_instance_id,
|
||||
"op": constants.PTP_PATCH_OPERATION_ADD
|
||||
}
|
||||
],
|
||||
headers=self.API_HEADERS,
|
||||
)
|
||||
self.assertEqual(response.content_type, "application/json")
|
||||
self.assertEqual(response.status_code, http_client.OK)
|
||||
|
||||
return ptp_instance_id
|
||||
|
||||
def test_host_ptp_instance_monitoring_assign_ok(self):
|
||||
self._assign_host_ptp_instance_monitoring_success()
|
||||
|
||||
def test_host_second_ptp_instance_monitoring_assign_failed(self):
|
||||
self._assign_host_ptp_instance_monitoring_success()
|
||||
|
||||
ptp_instance = dbutils.create_test_ptp_instance(
|
||||
name="test-instance2", service=constants.PTP_INSTANCE_TYPE_MONITORING
|
||||
)
|
||||
ptp_instance_id = ptp_instance["id"]
|
||||
response = self.patch_json(
|
||||
self.get_host_url(self.controller.uuid),
|
||||
[
|
||||
{
|
||||
"path": constants.PTP_INSTANCE_ARRAY_PATH,
|
||||
"value": ptp_instance_id,
|
||||
"op": constants.PTP_PATCH_OPERATION_ADD
|
||||
}
|
||||
],
|
||||
headers=self.API_HEADERS,
|
||||
expect_errors=True,
|
||||
)
|
||||
|
||||
self.assertEqual("application/json", response.content_type)
|
||||
self.assertEqual(response.status_code, http_client.BAD_REQUEST)
|
||||
self.assertIn(
|
||||
"Monitoring ptp instance already exists on host",
|
||||
response.json["error_message"]
|
||||
)
|
||||
|
||||
|
||||
class TestGetPtpInstance(BasePtpInstanceTestCase):
|
||||
def setUp(self):
|
||||
@@ -172,7 +228,8 @@ class TestListPtpInstance(BasePtpInstanceTestCase):
|
||||
services = [constants.PTP_INSTANCE_TYPE_PTP4L,
|
||||
constants.PTP_INSTANCE_TYPE_PHC2SYS,
|
||||
constants.PTP_INSTANCE_TYPE_SYNCE4L,
|
||||
constants.PTP_INSTANCE_TYPE_TS2PHC]
|
||||
constants.PTP_INSTANCE_TYPE_TS2PHC,
|
||||
constants.PTP_INSTANCE_TYPE_MONITORING]
|
||||
for service in services:
|
||||
name = '%s-%s' % (name_prefix, service)
|
||||
instance = dbutils.create_test_ptp_instance(name=name,
|
||||
|
Reference in New Issue
Block a user