diff --git a/keywords/cloud_platform/ssh/lab_connection_keywords.py b/keywords/cloud_platform/ssh/lab_connection_keywords.py index 10937f69..de45b5a6 100644 --- a/keywords/cloud_platform/ssh/lab_connection_keywords.py +++ b/keywords/cloud_platform/ssh/lab_connection_keywords.py @@ -78,9 +78,11 @@ class LabConnectionKeywords(BaseKeyword): """ lab_config = ConfigurationManager.get_lab_config() - + + if 'worker' in lab_config.get_node(hostname).get_type() : + return self.get_compute_ssh(hostname) + host_ip = lab_config.get_node(hostname).get_ip() - jump_host_config = None if lab_config.is_use_jump_server(): jump_host_config = lab_config.get_jump_host_configuration() @@ -92,7 +94,6 @@ class LabConnectionKeywords(BaseKeyword): ssh_port=lab_config.get_ssh_port(), jump_host=jump_host_config, ) - return connection def get_compute_ssh(self, compute_name: str) -> SSHConnection: diff --git a/keywords/cloud_platform/system/ptp/ptp_setup_executor_keywords.py b/keywords/cloud_platform/system/ptp/ptp_setup_executor_keywords.py index ca37434c..c69891fa 100644 --- a/keywords/cloud_platform/system/ptp/ptp_setup_executor_keywords.py +++ b/keywords/cloud_platform/system/ptp/ptp_setup_executor_keywords.py @@ -190,6 +190,17 @@ class PTPSetupExecutorKeywords(BaseKeyword): system_ptp_interface_show_output = system_ptp_interface_keywords.get_system_ptp_interface_show(interface_name) validate_str_contains(system_ptp_interface_show_output.get_ptp_interface().get_interface_names(), f"{ctrl1_hostname}/{interface}", f"assign ptp interface for {ctrl1_hostname}") + compute_0_interfaces = ptp_host_if.get_compute_0_interfaces() + comp0_hostname = "compute-0" + for interface in compute_0_interfaces: + if not interface : + continue + + system_host_if_ptp_keywords.system_host_if_ptp_assign(comp0_hostname, interface, interface_name) + system_ptp_interface_show_output = system_ptp_interface_keywords.get_system_ptp_interface_show(interface_name) + validate_str_contains(system_ptp_interface_show_output.get_ptp_interface().get_interface_names(), f"{comp0_hostname}/{interface}", f"assign ptp interface for {comp0_hostname}") + + ptp_interface_parameters = ptp_host_if.get_ptp_interface_parameter() if ptp_interface_parameters : system_ptp_interface_parameter_add_output = system_ptp_interface_keywords.system_ptp_interface_parameter_add(interface_name, ptp_interface_parameters) diff --git a/keywords/cloud_platform/system/ptp/ptp_verify_config.py b/keywords/cloud_platform/system/ptp/ptp_verify_config.py new file mode 100644 index 00000000..4ae15ffb --- /dev/null +++ b/keywords/cloud_platform/system/ptp/ptp_verify_config.py @@ -0,0 +1,623 @@ +import re +from typing import Any + +from framework.logging.automation_logger import get_logger +from framework.validation.validation import validate_equals +from keywords.base_keyword import BaseKeyword +from keywords.cloud_platform.ssh.lab_connection_keywords import LabConnectionKeywords +from keywords.linux.systemctl.systemctl_status_keywords import SystemCTLStatusKeywords +from keywords.ptp.cat.cat_ptp_cgu_keywords import CatPtpCguKeywords +from keywords.ptp.cat.cat_ptp_config_keywords import CatPtpConfigKeywords +from keywords.ptp.gnss_keywords import GnssKeywords +from keywords.ptp.pmc.pmc_keywords import PMCKeywords +from keywords.ptp.setup.object.ptp_host_interface_setup import PTPHostInterfaceSetup +from keywords.ptp.setup.ptp_setup_reader import PTPSetupKeywords + + +class PTPVerifyConfigKeywords(BaseKeyword): + """ + Verify all PTP configurations using given SSH connection. + + Attributes: + ssh_connection: An instance of an SSH connection. + """ + + def __init__(self, ssh_connection, ptp_setup_template_path): + """ + Initializes the PTPVerifyConfigKeywords with an SSH connection. + + Args: + ssh_connection: An instance of an SSH connection. + ptp_setup_template_path : ptp setup template path + """ + self.ssh_connection = ssh_connection + ptp_setup_keywords = PTPSetupKeywords() + ptp_setup = ptp_setup_keywords.generate_ptp_setup_from_template(ptp_setup_template_path) + + self.ptp4l_setup_list = ptp_setup.get_ptp4l_setup_list() + self.phc2sys_setup_list = ptp_setup.get_phc2sys_setup_list() + self.ts2phc_setup_list = ptp_setup.get_ts2phc_setup_list() + self.clock_setup_list = ptp_setup.get_clock_setup_list() + + self.ctrl0_hostname = "controller-0" + self.ctrl1_hostname = "controller-1" + self.comp0_hostname = "compute-0" + + def verify_all_ptp_configurations(self) -> None: + """ + verify all ptp configurations + + Returns: None + """ + self.verify_gnss_status() + + self.verify_sma_status() + + self.verify_systemctl_status() + + self.verify_ptp_pmc_values() + + self.verify_ptp_config_file_content() + + def verify_gnss_status(self) -> None: + """ + verify GNSS status + + Returns: None + """ + gnss_keywords = GnssKeywords() + + for ts2phc_instance_obj in self.ts2phc_setup_list: + ptp_host_ifs = ts2phc_instance_obj.get_ptp_interfaces() + instance_parameters = ts2phc_instance_obj.get_instance_parameters() + expected_gnss_port = gnss_keywords.extract_gnss_port(instance_parameters) + + if not expected_gnss_port: # No need to verify GNSS status if ts2phc.nmea_serialport not configured + get_logger().log_info("Validation skipped as expected; GNSS port is None") + continue + + for ptp_host_if in ptp_host_ifs: + controller_0_interfaces = ptp_host_if.get_controller_0_interfaces() + for interface in controller_0_interfaces: + if not interface: + continue + self.validate_gnss_status_on_hostname(self.ctrl0_hostname, interface, expected_gnss_port) + + controller_1_interfaces = ptp_host_if.get_controller_1_interfaces() + for interface in controller_1_interfaces: + if not interface: + continue + self.validate_gnss_status_on_hostname(self.ctrl1_hostname, interface, expected_gnss_port) + + compute_0_interfaces = ptp_host_if.get_compute_0_interfaces() + for interface in compute_0_interfaces: + if not interface: + continue + self.validate_gnss_status_on_hostname(self.comp0_hostname, interface, expected_gnss_port) + + def verify_sma_status(self) -> None: + """ + verify SMA status + + Returns: None + """ + for clock_instance_obj in self.clock_setup_list: + ptp_host_ifs = clock_instance_obj.get_ptp_interfaces() + + for ptp_host_if in ptp_host_ifs: + ptp_interface_parameters = ptp_host_if.get_ptp_interface_parameter() + + if "input" in ptp_interface_parameters: + controller_0_interfaces = ptp_host_if.get_controller_0_interfaces() + for interface in controller_0_interfaces: + if not interface: + continue + self.validate_sma_status_on_hostname(self.ctrl0_hostname, interface) + + controller_1_interfaces = ptp_host_if.get_controller_1_interfaces() + for interface in controller_1_interfaces: + if not interface: + continue + self.validate_sma_status_on_hostname(self.ctrl1_hostname, interface) + + compute_0_interfaces = ptp_host_if.get_compute_0_interfaces() + for interface in compute_0_interfaces: + if not interface: + continue + self.validate_sma_status_on_hostname(self.comp0_hostname, interface) + + def verify_systemctl_status(self) -> None: + """ + verify ptp systemctl ptp services status + + Returns: None + """ + systemctl_status_Keywords = SystemCTLStatusKeywords(self.ssh_connection) + + for ptp4l_instance_obj in self.ptp4l_setup_list: + name = ptp4l_instance_obj.get_name() + service_name = f"ptp4l@{name}.service" + + hostnames = ptp4l_instance_obj.get_instance_hostnames() + for hostname in hostnames: + systemctl_status_Keywords.verify_status_on_hostname(hostname, name, service_name) + + for phc2sys_instance_obj in self.phc2sys_setup_list: + name = phc2sys_instance_obj.get_name() + service_name = f"phc2sys@{name}.service" + + hostnames = phc2sys_instance_obj.get_instance_hostnames() + instance_parameters = phc2sys_instance_obj.get_instance_parameters() + for hostname in hostnames: + systemctl_status_Keywords.verify_ptp_status_and_instance_parameters_on_hostname(hostname, name, service_name, instance_parameters) + + for ts2phc_instance_obj in self.ts2phc_setup_list: + name = ts2phc_instance_obj.get_name() + service_name = f"ts2phc@{name}.service" + + hostnames = ts2phc_instance_obj.get_instance_hostnames() + for hostname in hostnames: + systemctl_status_Keywords.verify_status_on_hostname(hostname, name, service_name) + + def verify_ptp_config_file_content(self) -> None: + """ + Verify ptp config file content + + Returns: None + """ + for ptp4l_instance_obj in self.ptp4l_setup_list: + config_file = f"/etc/linuxptp/ptpinstance/ptp4l-{ptp4l_instance_obj.get_name()}.conf" + hostnames = ptp4l_instance_obj.get_instance_hostnames() + for hostname in hostnames: + self.validate_ptp_config_file_content(ptp4l_instance_obj, hostname, config_file) + + for ts2phc_instance_obj in self.ts2phc_setup_list: + config_file = f"/etc/linuxptp/ptpinstance/ts2phc-{ts2phc_instance_obj.get_name()}.conf" + hostnames = ts2phc_instance_obj.get_instance_hostnames() + for hostname in hostnames: + self.validate_ptp_config_file_content(ts2phc_instance_obj, hostname, config_file) + + def verify_ptp_pmc_values(self) -> None: + """ + verify ptp pmc values + + Returns: None + """ + for ptp4l_instance_obj in self.ptp4l_setup_list: + name = ptp4l_instance_obj.get_name() + config_file = f"/etc/linuxptp/ptpinstance/ptp4l-{name}.conf" + socket_file = f"/var/run/ptp4l-{name}" + + hostnames = ptp4l_instance_obj.get_instance_hostnames() + instance_parameters = ptp4l_instance_obj.get_instance_parameters() + ptp_role = ptp4l_instance_obj.get_ptp_role() + for hostname in hostnames: + + self.validate_port_data_set(hostname, config_file, socket_file, expected_port_state="MASTER") + + self.validate_get_domain(hostname, instance_parameters, config_file, socket_file) + + self.validate_parent_data_set(hostname, instance_parameters, config_file, socket_file) + + self.validate_time_properties_data_set(hostname, config_file, socket_file) + + self.validate_grandmaster_settings_np(hostname, ptp_role, config_file, socket_file) + + def validate_gnss_status_on_hostname(self, hostname: str, interface: str, expected_gnss_port: str) -> None: + """ + Validate GNSS status on the hostname + + Args: + hostname (str): The name of the host. + interface (str): The name of the ptp interface (e.g., "enp138s0f0"). + expected_gnss_port (str): Expected GNSS serial port value + + Returns: None + """ + gnss_keywords = GnssKeywords() + + input_name = "GNSS-1PPS" + expected_gnss_1pps_state = "valid" + expected_dpll_status = "locked_ho_acq" + + observed_gnss_port = gnss_keywords.get_gnss_serial_port_from_gnss_directory(hostname, interface) + if expected_gnss_port == observed_gnss_port: + pci_address = gnss_keywords.get_pci_slot_name(hostname, interface) + cgu_location = f"/sys/kernel/debug/ice/{pci_address}/cgu" + self.validate_cgu_input_and_dpll_status(hostname, cgu_location, input_name, expected_gnss_1pps_state, expected_dpll_status) + else: + get_logger().log_info(f"Validation skipped; GNSS port does not match for hostname {hostname} and interface {interface}") + get_logger().log_info(f"Expected GNSS port: {expected_gnss_port}") + get_logger().log_info(f"Observed GNSS port: {observed_gnss_port}") + + def validate_sma_status_on_hostname(self, hostname: str, interface: str) -> None: + """ + Validate SMA status on the hostname + + Args: + hostname (str): The name of the host. + interface (str): The name of the ptp interface (e.g., "enp138s0f0"). + + Returns: None + """ + gnss_keywords = GnssKeywords() + + input_name = "SMA1" + expected_sma1_state = "valid" + expected_dpll_status = "locked_ho_acq" + + pci_address = gnss_keywords.get_pci_slot_name(hostname, interface) + cgu_location = f"/sys/kernel/debug/ice/{pci_address}/cgu" + self.validate_cgu_input_and_dpll_status(hostname, cgu_location, input_name, expected_sma1_state, expected_dpll_status) + + def validate_cgu_input_and_dpll_status( + self, + hostname: str, + cgu_location: str, + input_name: str, + expected_input_state: str, + expected_status: str, + ) -> None: + """ + Validates the cgu and dpll status. + + Args: + hostname (str): The name of the host. + cgu_location (str): the cgu location. + input_name (str): the cgu input name. + expected_input_state (str): The expected cgu input state. + expected_status (str): expected of DPLL status. + + Returns: None + + Raises: + Exception: raised when validate fails + """ + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + cat_ptp_cgu_keywords = CatPtpCguKeywords(ssh_connection) + + ptp_cgu_output = cat_ptp_cgu_keywords.cat_ptp_cgu(cgu_location) + ptp_cgu_component = ptp_cgu_output.get_cgu_component() + + input_object = ptp_cgu_component.get_cgu_input(input_name) + state = input_object.get_state() + + eec_dpll_object = ptp_cgu_component.get_eec_dpll() + eec_dpll_current_reference = eec_dpll_object.get_current_reference() + eec_dpll_status = eec_dpll_object.get_status() + + pps_dpll_object = ptp_cgu_component.get_pps_dpll() + pps_dpll_current_reference = pps_dpll_object.get_current_reference() + pps_dpll_status = pps_dpll_object.get_status() + + if state == expected_input_state and eec_dpll_current_reference == input_name and eec_dpll_status == expected_status and pps_dpll_current_reference == input_name and pps_dpll_status == expected_status: + get_logger().log_info(f"Validation Successful - {input_name} state and DPLL status") + else: + get_logger().log_info(f"Validation Failed - {input_name} state and DPLL status") + get_logger().log_info(f"Expected {input_name}: {expected_input_state}") + get_logger().log_info(f"Observed {input_name}: {state}") + + get_logger().log_info(f"Expected EEC DPLL current refrence: {input_name}") + get_logger().log_info(f"Observed EEC DPLL current refrence: {eec_dpll_current_reference}") + get_logger().log_info(f"Expected EEC DPLL status: {expected_status}") + get_logger().log_info(f"Observed EEC DPLL status: {eec_dpll_status}") + + get_logger().log_info(f"Expected PPS DPLL current refrence: {input_name}") + get_logger().log_info(f"Observed PPS DPLL current refrence: {pps_dpll_current_reference}") + get_logger().log_info(f"Expected PPS DPLL status: {expected_status}") + get_logger().log_info(f"Observed PPS DPLL status: {pps_dpll_status}") + + raise Exception("Validation Failed") + + def validate_ptp_config_file_content( + self, + ptp_instance_obj: Any, + hostname: str, + config_file: str, + ) -> None: + """ + Validates the ptp config file content. + + Args: + ptp_instance_obj (Any) : PTP instance setup object + hostname (str): The name of the host. + config_file (str): the config file. + + Returns: None + + Raises: + Exception: raised when validate fails + """ + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + + cat_ptp_config_keywords = CatPtpConfigKeywords(ssh_connection) + cat_ptp_config_output = cat_ptp_config_keywords.cat_ptp_config(config_file) + get_pmc_get_default_data_set_object = cat_ptp_config_output.data_set_output.get_pmc_get_default_data_set_object() + + instance_parameters = ptp_instance_obj.get_instance_parameters() + parameters = self.parse_instance_parameters_string(instance_parameters) + + expected_boundary_clock_jbod = parameters.get("boundary_clock_jbod") + if expected_boundary_clock_jbod: + observed_boundary_clock_jbod = get_pmc_get_default_data_set_object.get_boundary_clock_jbod() + validate_equals(observed_boundary_clock_jbod, expected_boundary_clock_jbod, "boundary_clock_jbod value within PTP config file content") + + expected_dataset_comparison = parameters.get("dataset_comparison") + if expected_dataset_comparison: + observed_dataset_comparison = get_pmc_get_default_data_set_object.get_dataset_comparison() + validate_equals(observed_dataset_comparison, expected_dataset_comparison, "dataset_comparison value within PTP config file content") + + expected_domain_number = parameters.get("domainNumber") + if expected_domain_number: + observed_domain_number = get_pmc_get_default_data_set_object.get_domain_number() + validate_equals(observed_domain_number, expected_domain_number, "domainNumber value within PTP config file content") + + expected_priority1 = parameters.get("priority1") + if expected_priority1: + observed_priority1 = get_pmc_get_default_data_set_object.get_priority1() + validate_equals(observed_priority1, expected_priority1, "priority1 value within PTP config file content") + + expected_priority2 = parameters.get("priority2") + if expected_priority2: + observed_priority2 = get_pmc_get_default_data_set_object.get_priority2() + validate_equals(observed_priority2, expected_priority2, "priority2 value within PTP config file content") + + expected_tx_timestamp_timeout = parameters.get("tx_timestamp_timeout") + if expected_tx_timestamp_timeout: + observed_tx_timestamp_timeout = get_pmc_get_default_data_set_object.get_tx_timestamp_timeout() + validate_equals(observed_tx_timestamp_timeout, expected_tx_timestamp_timeout, "tx_timestamp_timeout value within PTP config file content") + + get_associated_interfaces = list( + map( + lambda ptp_host_if: ptp_host_if.get_controller_0_interfaces() if hostname == "controller-0" else ptp_host_if.get_controller_1_interfaces() if hostname == "controller-1" else ptp_host_if.get_compute_0_interfaces() if hostname == "compute-0" else [], + ptp_instance_obj.get_ptp_interfaces(), + ) + ) + expected_associated_interfaces = sum(get_associated_interfaces, []) + observed_associated_interfaces = cat_ptp_config_output.get_associated_interfaces() + validate_equals(observed_associated_interfaces, expected_associated_interfaces, "Associated interfaces within PTP config file content") + + def validate_port_data_set( + self, + hostname: str, + config_file: str, + socket_file: str, + expected_port_state: str, + ) -> None: + """ + Validates the get port data set. + + Args: + hostname (str): The name of the host. + config_file (str): the config file. + socket_file (str): the socket file. + expected_port_state (str): The current state of the port (e.g., MASTER, SLAVE, PASSIVE, LISTENING) + + Returns: None + + Raises: + Exception: raised when validate fails + """ + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + pmc_keywords = PMCKeywords(ssh_connection) + + get_port_data_set_output = pmc_keywords.pmc_get_port_data_set(config_file, socket_file) + get_pmc_get_port_data_set_object = get_port_data_set_output.get_pmc_get_port_data_set_object() + observed_port_identity = get_pmc_get_port_data_set_object.get_port_identity() + observed_port_state = get_pmc_get_port_data_set_object.get_port_state() + + validate_equals(observed_port_state, expected_port_state, "portState value within GET PORT_DATA_SET") + + def validate_get_domain( + self, + hostname: str, + instance_parameters: str, + config_file: str, + socket_file: str, + ) -> None: + """ + Validates the get domain number. + + Args: + hostname (str): The name of the host. + instance_parameters (str): instance parameters + config_file (str): the config file. + socket_file (str): the socket file. + + Returns: None + + Raises: + Exception: raised when validate fails + """ + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + pmc_keywords = PMCKeywords(ssh_connection) + + parameters = self.parse_instance_parameters_string(instance_parameters) + expected_domain_number = parameters.get("domainNumber") + + get_domain_output = pmc_keywords.pmc_get_domain(config_file, socket_file) + observed_domain_number = get_domain_output.get_pmc_get_domain_object().get_domain_number() + + validate_equals(observed_domain_number, expected_domain_number, "domainNumber value within GET DOMAIN") + + def validate_parent_data_set( + self, + hostname: str, + instance_parameters: str, + config_file: str, + socket_file: str, + ) -> None: + """ + Validates the get parent data set. + + Args: + hostname (str): The name of the host. + instance_parameters (str): instance parameters + config_file (str): the config file. + socket_file (str): the socket file. + + Returns: None + + Raises: + Exception: raised when validate fails + """ + + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + pmc_keywords = PMCKeywords(ssh_connection) + + # gm.ClockClass value is always 6 if it is in a good status. + expected_gm_clock_class = 6 + # gm.ClockAccuracy and gm.OffsetScaledLogVariance values can be overwritten using instance parameters, + # but for now, the default values are in use + expected_gm_clock_accuracy = "0x20" + expected_gm_offset_scaled_log_variance = "0x4e5d" + + parameters = self.parse_instance_parameters_string(instance_parameters) + expected_grandmaster_priority2 = parameters.get("priority2") + + get_parent_data_set_output = pmc_keywords.pmc_get_parent_data_set(config_file, socket_file) + get_parent_data_set_object = get_parent_data_set_output.get_pmc_get_parent_data_set_object() + observed_gm_clock_class = get_parent_data_set_object.get_gm_clock_class() + observed_gm_clock_accuracy = get_parent_data_set_object.get_gm_clock_accuracy() + observed_gm_offset_scaled_log_variance = get_parent_data_set_object.get_gm_offset_scaled_log_variance() + observed_grandmaster_priority2 = get_parent_data_set_object.get_grandmaster_priority2() + + validate_equals(observed_gm_clock_class, expected_gm_clock_class, "gm.ClockClass value within GET PARENT_DATA_SET") + validate_equals(observed_gm_clock_accuracy, expected_gm_clock_accuracy, "gm.ClockAccuracy value within GET PARENT_DATA_SET") + validate_equals(observed_gm_offset_scaled_log_variance, expected_gm_offset_scaled_log_variance, "gm.OffsetScaledLogVariance value within GET PARENT_DATA_SET") + validate_equals(observed_grandmaster_priority2, expected_grandmaster_priority2, "grandmasterPriority2 value within GET PARENT_DATA_SET") + + def validate_time_properties_data_set( + self, + hostname: str, + config_file: str, + socket_file: str, + ) -> None: + """ + Validates the get time properties data set. + + Args: + hostname (str): The name of the host. + config_file (str): the config file. + socket_file (str): the socket file. + + Returns: None + + Raises: + Exception: raised when validate fails + """ + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + pmc_keywords = PMCKeywords(ssh_connection) + + # default values if it is in a good status. + expected_current_utc_offset = 37 + expected_current_utc_offset_valid = 0 + expected_time_traceable = 1 + expected_frequency_traceable = 1 + + get_time_properties_data_set_output = pmc_keywords.pmc_get_time_properties_data_set(config_file, socket_file) + get_time_properties_data_set_object = get_time_properties_data_set_output.get_pmc_get_time_properties_data_set_object() + observed_current_utc_offset = get_time_properties_data_set_object.get_current_utc_offset() + observed_current_utc_off_set_valid = get_time_properties_data_set_object.get_current_utc_off_set_valid() + observed_time_traceable = get_time_properties_data_set_object.get_time_traceable() + observed_frequency_traceable = get_time_properties_data_set_object.get_frequency_traceable() + + validate_equals(observed_current_utc_offset, expected_current_utc_offset, "currentUtcOffset value within GET TIME_PROPERTIES_DATA_SET") + validate_equals(observed_current_utc_off_set_valid, expected_current_utc_offset_valid, "currentUtcOffsetValid value within GET TIME_PROPERTIES_DATA_SET") + validate_equals(observed_time_traceable, expected_time_traceable, "timeTraceable value within GET TIME_PROPERTIES_DATA_SET") + validate_equals(observed_frequency_traceable, expected_frequency_traceable, "frequencyTraceable value within GET TIME_PROPERTIES_DATA_SET") + + def validate_grandmaster_settings_np( + self, + hostname: str, + ptp_role: str, + config_file: str, + socket_file: str, + ) -> None: + """ + Validates the get grandmaster settings np. + + Args: + hostname (str): The name of the host. + ptp_role (str): state of the port (e.g., MASTER and SLAVE) + config_file (str): the config file. + socket_file (str): the socket file. + + Returns: None + + Raises: + Exception: raised when validate fails + """ + if ptp_role == "MASTER": + expected_clock_class = 6 + expected_clock_accuracy = "0x20" + expected_offset_scaled_log_variance = "0x4e5d" + expected_time_traceable = 1 + expected_frequency_traceable = 1 + expected_time_source = "0x20" + else: + expected_clock_class = 248 + expected_clock_accuracy = "0xfe" + expected_offset_scaled_log_variance = "0xffff" + expected_time_traceable = 0 + expected_frequency_traceable = 0 + expected_time_source = "0xa0" + + expected_current_utc_offset_valid = 0 + + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + + pmc_keywords = PMCKeywords(ssh_connection) + get_grandmaster_settings_np_output = pmc_keywords.pmc_get_grandmaster_settings_np(config_file, socket_file) + get_grandmaster_settings_np_object = get_grandmaster_settings_np_output.get_pmc_get_grandmaster_settings_np_object() + observed_clock_class = get_grandmaster_settings_np_object.get_clock_class() + observed_clock_accuracy = get_grandmaster_settings_np_object.get_clock_accuracy() + observed_offset_scaled_log_variance = get_grandmaster_settings_np_object.get_offset_scaled_log_variance() + observed_current_utc_offset_valid = get_grandmaster_settings_np_object.get_current_utc_off_set_valid() + observed_time_traceable = get_grandmaster_settings_np_object.get_time_traceable() + observed_frequency_traceable = get_grandmaster_settings_np_object.get_frequency_traceable() + observed_time_source = get_grandmaster_settings_np_object.get_time_source() + + validate_equals(observed_clock_class, expected_clock_class, "clockClass value within GET GRANDMASTER_SETTINGS_NP") + validate_equals(observed_clock_accuracy, expected_clock_accuracy, "clockAccuracy value within GET GRANDMASTER_SETTINGS_NP") + validate_equals(observed_offset_scaled_log_variance, expected_offset_scaled_log_variance, "offsetScaledLogVariance value within GET GRANDMASTER_SETTINGS_NP") + validate_equals(observed_current_utc_offset_valid, expected_current_utc_offset_valid, "currentUtcOffsetValid value within GET GRANDMASTER_SETTINGS_NP") + validate_equals(observed_time_traceable, expected_time_traceable, "timeTraceable value within GET GRANDMASTER_SETTINGS_NP") + validate_equals(observed_frequency_traceable, expected_frequency_traceable, "frequencyTraceable value within GET GRANDMASTER_SETTINGS_NP") + validate_equals(observed_time_source, expected_time_source, "timeSource value within GET GRANDMASTER_SETTINGS_NP") + + def parse_instance_parameters_string(self, instance_parameters: str) -> dict: + """ + Parses a string containing instance parameters and returns a dictionary. + + Args: + instance_parameters (str): A string containing instance parameters (e.g., "key1=value1 key2=value2"). + + Returns: + dict: A dictionary where keys are the instance parameter names and values are the + corresponding values. Returns an empty dictionary if the input string + is empty or doesn't contain any valid parameters. + Values are returned as integers if they consist only of digits, + otherwise as strings. + """ + parameters = {} + # Split the string by spaces to get individual key-value pairs + for item in instance_parameters.split(): + # boundary_clock_jbod=1 + match = re.search(r"([^=]+)=([^ ]+)", item) + if match: + key, value = match.groups() + # Try converting the value to an integer; if it fails, keep it as a string + try: + value = int(value) + except ValueError: + pass + parameters[key] = value + return parameters diff --git a/keywords/linux/systemctl/systemctl_status_keywords.py b/keywords/linux/systemctl/systemctl_status_keywords.py index a744a030..b6bdcb1d 100644 --- a/keywords/linux/systemctl/systemctl_status_keywords.py +++ b/keywords/linux/systemctl/systemctl_status_keywords.py @@ -1,5 +1,10 @@ +from multiprocessing import get_logger + from framework.ssh.ssh_connection import SSHConnection from keywords.base_keyword import BaseKeyword +from keywords.ptp.ptp4l.objects.ptp4l_status_output import PTP4LStatusOutput +from keywords.cloud_platform.ssh.lab_connection_keywords import LabConnectionKeywords +from starlingx.framework.validation.validation import validate_equals class SystemCTLStatusKeywords(BaseKeyword): @@ -14,7 +19,7 @@ class SystemCTLStatusKeywords(BaseKeyword): """ Gets the status of the given service name Args: - service_name (): the service name + service_name (str): the service name Returns: the output as a list of strings - this should be consumed by a parser for the given output type @@ -23,3 +28,75 @@ class SystemCTLStatusKeywords(BaseKeyword): self.validate_success_return_code(self.ssh_connection) return output + def verify_status_on_hostname(self, hostname :str, name : str, service_name : str) -> None: + """ + verify systemctl ptp service status on hostname + + Args: + hostname (str): The name of the host + name (str): name of instance (e.g., "phc1") + service_name (str): service name (e.g., "phc2sys@phc1.service") + + Returns: None + + Raises: + Exception: raised when validate fails + """ + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + + output = SystemCTLStatusKeywords(ssh_connection).get_status(service_name) + ptp_service_status_output = PTP4LStatusOutput(output) + + expected_service_status = "active (running)" + observed_service_status = ptp_service_status_output.get_ptp4l_object(name).get_active() + + if expected_service_status in observed_service_status : + get_logger().log_info(f"Validation Successful - systemctl status {service_name}") + else: + get_logger().log_info(f"Validation Failed - systemctl status {service_name}") + get_logger().log_info(f"Expected service status: {expected_service_status}") + get_logger().log_info(f"Observed service status: {observed_service_status}") + raise Exception("Validation Failed") + + + def verify_ptp_status_and_instance_parameters_on_hostname(self, hostname :str, name : str, service_name : str, instance_parameters : str) -> None: + """ + verify systemctl ptp service status and instance parameters on hostname + + Args: + hostname (str): The name of the host + name (str) : name of instance (e.g., "phc1") + service_name (str): service name (e.g., "phc2sys@phc1.service") + instance_parameters (str) : instance parameters + + Returns: None + + Raises: + Exception: raised when validate fails + """ + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + + + output = SystemCTLStatusKeywords(ssh_connection).get_status(service_name) + ptp_service_status_output = PTP4LStatusOutput(output) + + expected_service_status = "active (running)" + observed_service_status = ptp_service_status_output.get_ptp4l_object(name).get_active() + get_command = ptp_service_status_output.get_ptp4l_object(name).get_command() + + # From the input string "cmdline_opts='-s enpXXs0f2 -O -37 -m'" + # The extracted output string is '-s enpXXs0f2 -O -37 -m' + instance_parameter = eval(instance_parameters.split("=")[1]) + + if expected_service_status in observed_service_status and instance_parameter in get_command : + get_logger().log_info(f"Validation Successful - systemctl status {service_name}") + else: + get_logger().log_info(f"Validation Failed - systemctl status {service_name}") + get_logger().log_info(f"Expected service status: {expected_service_status}") + get_logger().log_info(f"Observed service status: {observed_service_status}") + get_logger().log_info(f"Expected instance parameter: {instance_parameter}") + get_logger().log_info(f"Observed instance parameter: {get_command}") + raise Exception("Validation Failed") + diff --git a/keywords/ptp/cat/cat_ptp_cgu_parser.py b/keywords/ptp/cat/cat_ptp_cgu_parser.py index bc8f4860..3934b889 100644 --- a/keywords/ptp/cat/cat_ptp_cgu_parser.py +++ b/keywords/ptp/cat/cat_ptp_cgu_parser.py @@ -65,7 +65,7 @@ class CatPtpCguParser: """ cgu: PtpCguComponentObject = None - match = re.match(r"Found (\S+) CGU", self.cat_ptp_cgu_output[0]) + match = re.match(r"(Found (\S+) CGU|Password: Found (\S+) CGU)", self.cat_ptp_cgu_output[0]) # Ask about this if match: chip_model = match.group(1) config_version_match = re.search( diff --git a/keywords/ptp/cat/objects/default_data_set_output.py b/keywords/ptp/cat/objects/default_data_set_output.py index 2e544674..60cb887a 100644 --- a/keywords/ptp/cat/objects/default_data_set_output.py +++ b/keywords/ptp/cat/objects/default_data_set_output.py @@ -65,7 +65,7 @@ class DefaultDataSetOutput: self.pmc_get_default_data_set_object.set_clock_identity(output_values["clockIdentity"]) if "domainNumber" in output_values: - self.pmc_get_default_data_set_object.set_domain_number(output_values["domainNumber"]) + self.pmc_get_default_data_set_object.set_domain_number(int(output_values["domainNumber"])) if "free_running" in output_values: self.pmc_get_default_data_set_object.set_free_running(int(output_values["free_running"])) @@ -109,6 +109,9 @@ class DefaultDataSetOutput: if "time_stamping" in output_values: self.get_pmc_get_default_data_set_object().set_time_stamping(output_values["time_stamping"]) + if "tx_timestamp_timeout" in output_values: + self.get_pmc_get_default_data_set_object().set_tx_timestamp_timeout(int(output_values["tx_timestamp_timeout"])) + if "uds_address" in output_values: self.get_pmc_get_default_data_set_object().set_uds_address(output_values["uds_address"]) diff --git a/keywords/ptp/gnss_keywords.py b/keywords/ptp/gnss_keywords.py index e5c3dc55..1db00cf9 100644 --- a/keywords/ptp/gnss_keywords.py +++ b/keywords/ptp/gnss_keywords.py @@ -1,3 +1,4 @@ +import re import time from multiprocessing import get_logger from time import sleep @@ -19,6 +20,77 @@ class GnssKeywords(BaseKeyword): Initializes the GnssKeywords. """ + def get_pci_slot_name(self, hostname: str, interface: str) -> str: + """ + Retrieves the PCI_SLOT_NAME from the uevent file for a given PTP interface. + + Args: + hostname (str) : The name of the host + interface (str): The name of the ptp interface (e.g., "enp138s0f0"). + + Returns: + str: The PCI slot name if found, otherwise None. + + Raises: + Exception: raised when PCI_SLOT_NAME not found + """ + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + + # The GNSS signal will always be on port 0 of the NIC, even if ts2phc uses ports 1, 2, 3, and so on. + interface_name = f"{interface[:-1]}0" + uevent_path = f"/sys/class/net/{interface_name}/device/uevent" + + uevent_content = ssh_connection.send(f"grep PCI_SLOT_NAME {uevent_path}") + + # Use regex to find the PCI_SLOT_NAME + match = re.search(r"PCI_SLOT_NAME=(.*)", " ".join(uevent_content)) + if match: + return match.group(1).strip() # Return the captured value, removing leading/trailing spaces + else: + raise Exception(f"PCI_SLOT_NAME not found in {uevent_path}") + + def get_gnss_serial_port_from_gnss_directory(self, hostname: str, interface: str) -> str: + """ + Get GNSS serial port from the specified gnss directory. + + Args: + hostname (str) : The name of the host + interface (str): The name of the PTP interface (e.g., "enp138s0f0"). + + Returns: + str: The GNSS serial port value (e.g., "gnss0") if found, otherwise None. + """ + lab_connect_keywords = LabConnectionKeywords() + ssh_connection = lab_connect_keywords.get_ssh_for_hostname(hostname) + + pci_address = self.get_pci_slot_name(hostname, interface) + + gnss_dir = f"/sys/bus/pci/devices/{pci_address}/gnss" + + contents = ssh_connection.send(f"ls {gnss_dir}") + if not contents: + get_logger().log_info(f"The directory {gnss_dir} is empty.") + return None + + return " ".join(contents).strip() # Return the captured value in str, removing leading/trailing spaces + + def extract_gnss_port(self, instance_parameters: str) -> str: + """ + Extracts the GNSS serial port value from a ts2phc.nmea_serialport configuration string using regex. + + Args: + instance_parameters (str): The string containing the ts2phc.nmea_serialport setting. + + Returns: + str: The GNSS serial port value (e.g., "gnss0") if found, otherwise None. + """ + match = re.search(r"ts2phc\.nmea_serialport\s*=\s*/dev/([^ ]*)", instance_parameters) + if match: + return match.group(1) + else: + return None + def gnss_power_on(self, hostname: str, nic: str) -> None: """ Power on gnss @@ -88,6 +160,8 @@ class GnssKeywords(BaseKeyword): expected_pps_dpll_status (list): expected list of PPS DPLL status values. timeout (int): The maximum time (in seconds) to wait for the match. polling_sleep_time (int): The time period to wait to receive the expected output. + + Returns: None Raises: TimeoutError: raised when validate does not equal in the required time diff --git a/keywords/ptp/pmc/objects/pmc_get_default_data_set_object.py b/keywords/ptp/pmc/objects/pmc_get_default_data_set_object.py index 6e97df9d..04e7309f 100644 --- a/keywords/ptp/pmc/objects/pmc_get_default_data_set_object.py +++ b/keywords/ptp/pmc/objects/pmc_get_default_data_set_object.py @@ -232,7 +232,7 @@ class PMCGetDefaultDataSetObject: """ self.clock_identity = clock_identity - def get_domain_number(self) -> str: + def get_domain_number(self) -> int: """ Getter for domain_number @@ -242,7 +242,7 @@ class PMCGetDefaultDataSetObject: """ return self.domain_number - def set_domain_number(self, domain_number: str): + def set_domain_number(self, domain_number: int): """ Setter for domain_number diff --git a/keywords/ptp/pmc/objects/pmc_get_default_data_set_output.py b/keywords/ptp/pmc/objects/pmc_get_default_data_set_output.py index c7029e99..9ad561f8 100644 --- a/keywords/ptp/pmc/objects/pmc_get_default_data_set_output.py +++ b/keywords/ptp/pmc/objects/pmc_get_default_data_set_output.py @@ -73,7 +73,7 @@ class PMCGetDefaultDataSetOutput: self.pmc_get_default_data_set_object.set_clock_identity(output_values["clockIdentity"]) if "domainNumber" in output_values: - self.pmc_get_default_data_set_object.set_domain_number(output_values["domainNumber"]) + self.pmc_get_default_data_set_object.set_domain_number(int(output_values["domainNumber"])) if "free_running" in output_values: self.pmc_get_default_data_set_object.set_free_running(int(output_values["free_running"])) diff --git a/keywords/ptp/pmc/pmc_keywords.py b/keywords/ptp/pmc/pmc_keywords.py index a17ffa12..e9ddf943 100644 --- a/keywords/ptp/pmc/pmc_keywords.py +++ b/keywords/ptp/pmc/pmc_keywords.py @@ -8,6 +8,7 @@ from keywords.ptp.pmc.objects.pmc_get_parent_data_set_output import PMCGetParent from keywords.ptp.pmc.objects.pmc_get_port_data_set_output import PMCGetPortDataSetOutput from keywords.ptp.pmc.objects.pmc_get_time_properties_data_set_output import PMCGetTimePropertiesDataSetOutput from keywords.ptp.pmc.objects.pmc_get_time_status_np_output import PMCGetTimeStatusNpOutput +from keywords.ptp.pmc.objects.pmc_get_port_data_set_output import PMCGetPortDataSetOutput class PMCKeywords(BaseKeyword): @@ -153,16 +154,12 @@ class PMCKeywords(BaseKeyword): def pmc_get_default_data_set(self, config_file: str, socket_file: str, unicast: bool = True, boundry_clock: int = 0) -> PMCGetDefaultDataSetOutput: """ Gets the default data set - Args: config_file (str): the config file socket_file (str): the socket file unicast (bool): true to use unicast boundry_clock (int): the boundry clock - Returns: - PMCGetDefaultDataSetOutput: the default dataset output - Example: PMCKeywords(ssh_connection).pmc_get_default_data_set('/etc/linuxptp/ptpinstance/ptp4l-ptp5.conf', ' /var/run/ptp4l-ptp5') """ @@ -172,6 +169,24 @@ class PMCKeywords(BaseKeyword): pmc_get_default_data_set_output = PMCGetDefaultDataSetOutput(output) return pmc_get_default_data_set_output + def pmc_get_port_data_set(self, config_file: str, socket_file: str, unicast: bool = True, boundry_clock: int = 0) -> PMCGetPortDataSetOutput: + """ + Gets the port data set + Args: + config_file (str): the config file + socket_file (str): the socket file + unicast (bool): true to use unicast + boundry_clock (int): the boundry clock + + Example: PMCKeywords(ssh_connection).pmc_get_port_data_set('/etc/linuxptp/ptpinstance/ptp4l-ptp5.conf', ' /var/run/ptp4l-ptp5') + + """ + cmd = f"pmc {'-u' if unicast else ''} -b {boundry_clock} -f {config_file} -s {socket_file} 'GET PORT_DATA_SET'" + + output = self.ssh_connection.send_as_sudo(cmd) + pmc_get_port_data_set_output = PMCGetPortDataSetOutput(output) + return pmc_get_port_data_set_output + def pmc_get_domain(self, config_file: str, socket_file: str, unicast: bool = True, boundry_clock: int = 0) -> PMCGetDomainOutput: """ Gets the domain diff --git a/keywords/ptp/ptp4l/objects/ptp4l_status_output.py b/keywords/ptp/ptp4l/objects/ptp4l_status_output.py index 98cfaf76..b99b9de8 100644 --- a/keywords/ptp/ptp4l/objects/ptp4l_status_output.py +++ b/keywords/ptp/ptp4l/objects/ptp4l_status_output.py @@ -80,7 +80,7 @@ class PTP4LStatusOutput: """ Getter for ptp4l object with the given service name Args: - service_name (): the name of the service + service_name (str): the name of the service (e.g., "phc1") Returns: PTP4LStatusObject diff --git a/keywords/ptp/ptp4l/ptp4l_status_parser.py b/keywords/ptp/ptp4l/ptp4l_status_parser.py index f963f4cf..27603cc9 100644 --- a/keywords/ptp/ptp4l/ptp4l_status_parser.py +++ b/keywords/ptp/ptp4l/ptp4l_status_parser.py @@ -46,7 +46,7 @@ class PTP4LStatusParser: for line in self.ptp4l_status_output: line = line.strip() if line.startswith('●'): # we have a new service to get values for - service_name = line.split('@')[1].split(' ')[0] # format ptp4l@ptp1.service - Precision Time Protocol (PTP) service + service_name = line.split('@')[1].split(' ')[0].replace('.service','') # format ptp4l@ptp1.service - Precision Time Protocol (PTP) service services[service_name] = {} current_service = services[service_name] elif line.startswith('└─') and current_service is not None: diff --git a/keywords/ptp/setup/object/ptp4l_setup.py b/keywords/ptp/setup/object/ptp4l_setup.py index 28d1d7af..e6db2176 100644 --- a/keywords/ptp/setup/object/ptp4l_setup.py +++ b/keywords/ptp/setup/object/ptp4l_setup.py @@ -39,6 +39,12 @@ class PTP4LSetup: ptp_interfaces.append(ptp_host_ifs_dict[ptp_interface_name]) self.ptp_interfaces = ptp_interfaces + self.ptp_role = "MASTER" # default value is MASTER + if "ptp_role" in setup_dict: + self.ptp_role = setup_dict["ptp_role"] + + self.port_state = setup_dict.get("port_state") + def __str__(self) -> str: """ String representation of this object. @@ -99,3 +105,22 @@ class PTP4LSetup: if ptp_interface.get_name() == interface_name: return ptp_interface raise Exception(f"There is no ptp interface named {interface_name} in the ptp4l setup.") + + + def get_ptp_role(self) -> str: + """ + Gets the ptp role + + Returns: + str: ptp role + """ + return self.ptp_role + + def get_port_state(self) -> str: + """ + Gets the port state + + Returns: + str: port state + """ + return self.port_state diff --git a/keywords/ptp/setup/object/ptp_host_interface_setup.py b/keywords/ptp/setup/object/ptp_host_interface_setup.py index e0e0a8a7..e7d86ce4 100644 --- a/keywords/ptp/setup/object/ptp_host_interface_setup.py +++ b/keywords/ptp/setup/object/ptp_host_interface_setup.py @@ -30,6 +30,10 @@ class PTPHostInterfaceSetup: raise Exception(f"The ptp host interface entry {self.name} must have controller_1_interfaces defined.") self.controller_1_interfaces = setup_dict["controller_1_interfaces"] + self.compute_0_interfaces = None + if "compute_0_interfaces" in setup_dict: + self.compute_0_interfaces = setup_dict.get("compute_0_interfaces") + def __str__(self): """ String representation of this object. @@ -75,3 +79,12 @@ class PTPHostInterfaceSetup: List[str]: The controller_1_interfaces of this ptp host interface setup. """ return self.controller_1_interfaces + + def get_compute_0_interfaces(self) -> List[str]: + """ + Gets the compute_0_interfaces of this ptp host interface setup. + + Returns: + List[str]: The compute_0_interfaces of this ptp host interface setup. + """ + return self.compute_0_interfaces diff --git a/resources/ptp/setup/ptp_setup_template.json5 b/resources/ptp/setup/ptp_setup_template.json5 index 07ed8b24..8dd309e0 100644 --- a/resources/ptp/setup/ptp_setup_template.json5 +++ b/resources/ptp/setup/ptp_setup_template.json5 @@ -211,14 +211,14 @@ name: "clock1if1", controller_0_interfaces: ["{{ controller_0.nic1.conn_to_spirent }}"], controller_1_interfaces: [], - ptp_interface_parameter : "sma1={{ controller_0.nic1.sma1.name }}", + ptp_interface_parameter : "sma1={{ controller_0.nic1.sma1.is_input_or_output }}", }, { name: "clock1if2", controller_0_interfaces: ["{{ controller_0.nic2.base_port }}"], controller_1_interfaces: [], - ptp_interface_parameter : "sma1={{ controller_0.nic2.sma1.name }}", + ptp_interface_parameter : "sma1={{ controller_0.nic2.sma1.is_input_or_output }}", }, ], diff --git a/resources/ptp/setup/ptp_setup_template_with_compute.json5 b/resources/ptp/setup/ptp_setup_template_with_compute.json5 index 9c0e7328..e4e6bbb1 100644 --- a/resources/ptp/setup/ptp_setup_template_with_compute.json5 +++ b/resources/ptp/setup/ptp_setup_template_with_compute.json5 @@ -5,51 +5,54 @@ { name: "ptp1", - instance_hostnames : ["controller-0", "controller-1","compute-0"] , - instance_parameters: "tx_timestamp_timeout=700 domainNumber=24 dataset_comparison=G.8275.x priority2=100 boundary_clock_jbod=1", + instance_hostnames : ["compute-0", "controller-0", "controller-1"] , + instance_parameters: "priority2=100 tx_timestamp_timeout=700 boundary_clock_jbod=1 domainNumber=24 dataset_comparison=G.8275.x", ptp_interface_names: [ "ptp1if1", "ptp1if2", - ] + ], + ptp_role: "MASTER" }, { name: "ptp2", instance_hostnames : [], - instance_parameters: "dataset_comparison=G.8275.x domainNumber=24 tx_timestamp_timeout=700 boundary_clock_jbod=1 priority2=110", + instance_parameters: "priority2=110 dataset_comparison=G.8275.x tx_timestamp_timeout=700 boundary_clock_jbod=1 domainNumber=24", ptp_interface_names: [ "ptp2if1", "ptp2if2" ], + ptp_role: "MASTER" }, { name: "ptp3", - instance_hostnames : ["controller-0"], - instance_parameters: "dataset_comparison=G.8275.x domainNumber=24 tx_timestamp_timeout=700 boundary_clock_jbod=1 priority2=100", + instance_hostnames : ["controller-0", "compute-0"], + instance_parameters: "priority2=100 dataset_comparison=G.8275.x tx_timestamp_timeout=700 boundary_clock_jbod=1 domainNumber=24", ptp_interface_names: [ "ptp3if1", "ptp3if2" ], + ptp_role: "MASTER" }, { name: "ptp4", instance_hostnames : ["controller-1"], - instance_parameters: "priority2=110 dataset_comparison=G.8275.x domainNumber=24 tx_timestamp_timeout=700 boundary_clock_jbod=1", + instance_parameters: "boundary_clock_jbod=1 tx_timestamp_timeout=700 priority2=110 dataset_comparison=G.8275.x domainNumber=24", ptp_interface_names: [ "ptp4if1", "ptp4if2" ], + ptp_role: "SLAVE" } ], phc2sys : [ - { name: "phc1", - instance_hostnames : ["controller-0", "controller-1","compute-0"], + instance_hostnames : ["controller-0", "compute-0", "controller-1"], instance_parameters: "cmdline_opts='-s {{controller_0.nic1.conn_to_proxmox}} -O -37 -m'", ptp_interface_names: [ "phc1if1", @@ -68,7 +71,7 @@ { name: "phc3", instance_hostnames : [], - instance_parameters: "uds_address=/var/run/ptp4l-ptp3 domainNumber=24", + instance_parameters: "domainNumber=24 uds_address=/var/run/ptp4l-ptp3", ptp_interface_names: [ "phc3if1", ], @@ -77,7 +80,7 @@ { name: "phc4", instance_hostnames : [], - instance_parameters: "uds_address=/var/run/ptp4l-ptp4 domainNumber=24", + instance_parameters: "domainNumber=24 uds_address=/var/run/ptp4l-ptp4", ptp_interface_names: [ "phc4if1", ], @@ -86,41 +89,35 @@ ], ts2phc : [ - { name: "ts1", - instance_hostnames : ["controller-0"], + instance_hostnames : ["controller-1", "controller-0", "compute-0"], instance_parameters: "ts2phc.nmea_serialport=/dev/gnss0", ptp_interface_names: [ "ts1if1", ], - } - + }, ], - clock : [ - { name: "clock1", - instance_hostnames : ["controller-0"], + instance_hostnames : ["controller-0","compute-0"], instance_parameters: "", ptp_interface_names: [ "clock1if1", "clock1if2", ], - } - + }, ], }, ptp_host_ifs: [ - { name: "ptp1if1", - controller_0_interfaces: ["{{ controller_0.nic1.nic_connection.interface }}"], // Connection to controller1-nic1 - controller_1_interfaces: ["{{ controller_1.nic1.nic_connection.interface }}"], // Connection to controller0-nic1 - compute_0_interfaces: [], + controller_0_interfaces: ["{{ controller_0.nic1.nic_connection.interface }}"], + controller_1_interfaces: ["{{ controller_1.nic1.nic_connection.interface }}"], + compute_0_interfaces: ["{{ compute_0.nic1.nic_connection.interface }}"], ptp_interface_parameter : "", }, @@ -150,9 +147,9 @@ { name: "ptp3if1", - controller_0_interfaces: ["{{ controller_0.nic2.nic_connection.interface }}"], // Connection to Controller1 Nic 2 + controller_0_interfaces: ["{{ controller_0.nic2.nic_connection.interface }}"], controller_1_interfaces: [], - compute_0_interfaces: [], + compute_0_interfaces: ["{{ compute_0.nic2.nic_connection.interface }}"], ptp_interface_parameter : "", }, @@ -167,7 +164,7 @@ { name: "ptp4if1", controller_0_interfaces: [], - controller_1_interfaces: ["{{ controller_0.nic2.nic_connection.interface }}"], // Connection to Controller1 Nic 2 + controller_1_interfaces: ["{{ controller_1.nic2.nic_connection.interface }}"], compute_0_interfaces: [], ptp_interface_parameter : "", }, @@ -207,16 +204,16 @@ { name: "phc4if1", controller_0_interfaces: [], - controller_1_interfaces: ["{{ controller_1.nic1.base_port }}", "{{ controller_1.nic2.base_port }}"], + controller_1_interfaces: ["{{ controller_1.nic2.base_port }}"], compute_0_interfaces: [], ptp_interface_parameter :"", }, { name: "ts1if1", - controller_0_interfaces: ["{{ controller_0.nic1.conn_to_proxmox }}", "{{ controller_0.nic2.base_port }}"], - controller_1_interfaces: [], - compute_0_interfaces: [], + controller_0_interfaces: ["{{ controller_0.nic2.base_port }}", "{{ controller_0.nic1.conn_to_proxmox }}"], + controller_1_interfaces: ["{{ controller_1.nic1.conn_to_proxmox }}"], + compute_0_interfaces: ["{{ compute_0.nic1.nic_connection.interface }}", "{{ compute_0.nic2.nic_connection.interface }}"], ptp_interface_parameter : "", }, @@ -224,17 +221,15 @@ name: "clock1if1", controller_0_interfaces: ["{{ controller_0.nic1.conn_to_proxmox }}"], controller_1_interfaces: [], - compute_0_interfaces: [], - ptp_interface_parameter : "sma1={{ controller_0.nic1.sma1.name }}", + compute_0_interfaces: ["{{ compute_0.nic1.nic_connection.interface }}"], + ptp_interface_parameter : "sma1={{ controller_0.nic1.sma1.is_input_or_output }}", }, - { name: "clock1if2", controller_0_interfaces: ["{{ controller_0.nic2.base_port }}"], controller_1_interfaces: [], - compute_0_interfaces: [], - ptp_interface_parameter : "sma1={{ controller_0.nic2.sma1.name }}", + compute_0_interfaces: ["{{ compute_0.nic2.nic_connection.interface }}"], + ptp_interface_parameter : "sma1={{ controller_0.nic2.sma1.is_input_or_output }}", }, ], - } diff --git a/unit_tests/parser/ptp/ptp4l_status_parser_test.py b/unit_tests/parser/ptp/ptp4l_status_parser_test.py index 000d19f5..82a895fe 100644 --- a/unit_tests/parser/ptp/ptp4l_status_parser_test.py +++ b/unit_tests/parser/ptp/ptp4l_status_parser_test.py @@ -33,7 +33,7 @@ def test_ptp4l_service_status_output_parser(): ptp4l_service_status_output = PTP4LStatusOutput(ptp4l_status_output) # validate first service 'ptp1.service' - ptp4l_status_object = ptp4l_service_status_output.get_ptp4l_object('ptp1.service') + ptp4l_status_object = ptp4l_service_status_output.get_ptp4l_object('ptp1') assert ptp4l_status_object.get_active() == 'active (running) since Mon 2025-02-10 18:36:34 UTC; 3 days ago' assert ptp4l_status_object.get_c_group() == '/system.slice/system-ptp4l.slice/ptp4l@ptp1.service' @@ -46,7 +46,7 @@ def test_ptp4l_service_status_output_parser(): assert ptp4l_status_object.get_tasks() == '1 (limit: 150897)' # validate second service 'ptp3.service - ptp4l_status_object = ptp4l_service_status_output.get_ptp4l_object('ptp3.service') + ptp4l_status_object = ptp4l_service_status_output.get_ptp4l_object('ptp3') assert ptp4l_status_object.get_active() == 'active (running) since Wed 2025-02-12 16:22:23 UTC; 2 days ago' assert ptp4l_status_object.get_c_group() == '/system.slice/system-ptp4l.slice/ptp4l@ptp3.service'