From 217f41dab581341742bf474174d00073377211be Mon Sep 17 00:00:00 2001 From: croy Date: Thu, 27 Feb 2025 16:58:15 -0500 Subject: [PATCH] PTPSetup and associated reader Change-Id: I2a878c41cdc552a387623f06c2890b25f4403a75 Signed-off-by: croy --- config/ptp/files/default.json5 | 10 +- config/ptp/objects/ptp_config.py | 18 +- config/ptp/objects/ptp_host.py | 20 +- config/ptp/objects/ptp_nic.py | 14 ++ keywords/ptp/setup/object/clock_setup.py | 101 ++++++++ keywords/ptp/setup/object/phc2sys_setup.py | 86 +++++++ keywords/ptp/setup/object/ptp4l_setup.py | 86 +++++++ .../setup/object/ptp_host_interface_setup.py | 77 ++++++ keywords/ptp/setup/object/ptp_setup.py | 110 +++++++++ keywords/ptp/setup/object/ts2phc_setup.py | 86 +++++++ keywords/ptp/setup/ptp_setup_reader.py | 40 ++++ resources/ptp/setup/ptp_setup_template.json5 | 225 ++++++++++++++++++ unit_tests/config/ptp/ptp_config_test.py | 5 +- .../keyword/ptp/ptp_setup_reader_test.py | 32 +++ 14 files changed, 900 insertions(+), 10 deletions(-) create mode 100644 keywords/ptp/setup/object/clock_setup.py create mode 100644 keywords/ptp/setup/object/phc2sys_setup.py create mode 100644 keywords/ptp/setup/object/ptp4l_setup.py create mode 100644 keywords/ptp/setup/object/ptp_host_interface_setup.py create mode 100644 keywords/ptp/setup/object/ptp_setup.py create mode 100644 keywords/ptp/setup/object/ts2phc_setup.py create mode 100644 keywords/ptp/setup/ptp_setup_reader.py create mode 100644 resources/ptp/setup/ptp_setup_template.json5 create mode 100644 unit_tests/keyword/ptp/ptp_setup_reader_test.py diff --git a/config/ptp/files/default.json5 b/config/ptp/files/default.json5 index 02c7137d..3185fda0 100644 --- a/config/ptp/files/default.json5 +++ b/config/ptp/files/default.json5 @@ -13,7 +13,7 @@ // Can be assigned to any interface of the NIC connected to GNSS and with any NIC on the same host. // By default, we use the first port. base_port: "enp81s0f0", - conn_to_ctr1_nic1: "enp81s0f1", + conn_to_ctrl1_nic1: "enp81s0f1", // Optional Spirent config. conn_to_spirent: "", @@ -33,7 +33,7 @@ // Can be assigned to any interface of the NIC connected to GNSS and with any NIC on the same host. // By default, we use the first port. base_port: "", - conn_to_ctr1_nic2: "", + conn_to_ctrl1_nic2: "", // Optional Spirent config. conn_to_spirent: "", @@ -56,7 +56,7 @@ // Can be assigned to any interface of the NIC connected to GNSS and with any NIC on the same host. // By default, we use the first port. base_port: "", - conn_to_ctr0_nic1: "", + conn_to_ctrl0_nic1: "", // Optional Spirent config. conn_to_spirent: "", @@ -75,8 +75,8 @@ // This port for TS2PHC and PHC2SYS. // Can be assigned to any interface of the NIC connected to GNSS and with any NIC on the same host. // By default, we use the first port. - base_port: "", - conn_to_ctr0_nic2: "", + base_port: "enp82s0f0", + conn_to_ctrl0_nic2: "", // Optional Spirent config. conn_to_spirent: "", diff --git a/config/ptp/objects/ptp_config.py b/config/ptp/objects/ptp_config.py index df757b20..738ae695 100644 --- a/config/ptp/objects/ptp_config.py +++ b/config/ptp/objects/ptp_config.py @@ -1,4 +1,4 @@ -from typing import List +from typing import Dict, List import json5 @@ -33,6 +33,22 @@ class PTPConfig: """ return "PTPConfig" + def get_all_hosts_dictionary(self) -> Dict[str, Dict]: + """ + This function will return a dictionary view of the PTPConfig. + + This is mostly used for substitution in JINJA templates. + + Returns: + Dict[str, Dict]: Dictionary representation + + """ + dictionary_view = {} + for host in self.get_all_hosts(): + dictionary_view[host.get_name()] = host.get_all_nics_dictionary() + + return dictionary_view + def get_all_hosts(self) -> List[PTPHost]: """ Getter for the PTP information for every host defined in the config. diff --git a/config/ptp/objects/ptp_host.py b/config/ptp/objects/ptp_host.py index ea552e7f..9be4c4fb 100644 --- a/config/ptp/objects/ptp_host.py +++ b/config/ptp/objects/ptp_host.py @@ -32,6 +32,22 @@ class PTPHost: """ return f"PTPHost - {self.name}" + def get_all_nics_dictionary(self) -> Dict[str, Dict]: + """ + This function will return a dictionary view of the PTPHost. + + This is mostly used for substitution in JINJA templates. + + Returns: + Dict[str, Dict]: Dictionary representation + + """ + dictionary_view = {} + for nic in self.get_all_nics(): + dictionary_view[nic.get_name()] = nic.to_dictionary() + + return dictionary_view + def get_name(self) -> str: """ Getter for the name of this ptp_host. @@ -65,6 +81,4 @@ class PTPHost: for nic in self.nics: if nic.get_name() == nic_name: return nic - raise Exception( - f"There is no PTP NIC called {nic_name} associated with the host {self.name} in the PTP config." - ) + raise Exception(f"There is no PTP NIC called {nic_name} associated with the host {self.name} in the PTP config.") diff --git a/config/ptp/objects/ptp_nic.py b/config/ptp/objects/ptp_nic.py index 445278ed..9cb95dfc 100644 --- a/config/ptp/objects/ptp_nic.py +++ b/config/ptp/objects/ptp_nic.py @@ -29,6 +29,8 @@ class PTPNic: self.conn_to_spirent = None self.spirent_port = None + self.nic_dictionary = nic_dict + if "gnss_switch_port" in nic_dict and nic_dict["gnss_switch_port"]: self.gnss_switch_port = nic_dict["gnss_switch_port"] @@ -74,6 +76,18 @@ class PTPNic: """ return f"PTPNic - {self.name}" + def to_dictionary(self) -> Dict[str, str]: + """ + This function will return a dictionary view of the PTPNic. + + This is mostly used for substitution in JINJA templates. + + Returns: + Dict[str, str]: Dictionary representation + + """ + return self.nic_dictionary + def get_name(self) -> str: """ Gets the Name of this PTP NIC. diff --git a/keywords/ptp/setup/object/clock_setup.py b/keywords/ptp/setup/object/clock_setup.py new file mode 100644 index 00000000..02881b30 --- /dev/null +++ b/keywords/ptp/setup/object/clock_setup.py @@ -0,0 +1,101 @@ +from typing import Any, Dict, List + +from keywords.ptp.setup.object.ptp_host_interface_setup import PTPHostInterfaceSetup + + +class ClockSetup: + """ + Class models a clock setup + """ + + def __init__(self, setup_dict: Dict[str, Any], ptp_host_ifs_dict: Dict[str, PTPHostInterfaceSetup]): + """ + Constructor. + + Args: + setup_dict (Dict[str, Any]): The dictionary read from the JSON setup template file associated with this clock setup. + ptp_host_ifs_dict (Dict[str, PTPHostInterfaceSetup]): The dictionary that maps the name of thePTPHostInterfaceSetup to its associated object. + + """ + if "name" not in setup_dict: + raise Exception("Every clock entry should have a name.") + self.name = setup_dict["name"] + + if "instance_hostnames" not in setup_dict: + raise Exception(f"The clock entry {self.name} must have instance_hostnames defined.") + self.instance_hostnames = setup_dict["instance_hostnames"] + + if "instance_parameters" not in setup_dict: + raise Exception(f"The clock entry {self.name} must have instance_parameters defined.") + self.instance_parameters = setup_dict["instance_parameters"] + + if "ptp_interface_names" not in setup_dict: + raise Exception(f"The clock entry {self.name} must have ptp_interface_names defined.") + + ptp_interfaces = [] + for ptp_interface_name in setup_dict["ptp_interface_names"]: + if ptp_interface_name not in ptp_host_ifs_dict: + raise Exception(f"The ptp_host_if entry {ptp_interface_name} must be defined.") + ptp_interfaces.append(ptp_host_ifs_dict[ptp_interface_name]) + self.ptp_interfaces = ptp_interfaces + + def __str__(self): + """ + String representation of this object. + + Returns: + str: String representation of this object. + + """ + return self.get_name() + + def get_name(self) -> str: + """ + Gets the name of this clock setup. + + Returns: + str: The name of this clock setup. + """ + return self.name + + def get_instance_hostnames(self) -> list[str]: + """ + Gets the list of instance hostnames. + + Returns: + list[str]: The list of instance hostnames. + """ + return self.instance_hostnames + + def get_instance_parameters(self) -> str: + """ + Gets the instance parameters as a string. + + Returns: + str: The instance parameters as a string + """ + return self.instance_parameters + + def get_ptp_interfaces(self) -> List[PTPHostInterfaceSetup]: + """ + Gets the list of PTP interfaces. + + Returns: + List[PTPHostInterfaceSetup]: The list of PTP interfaces. + """ + return self.ptp_interfaces + + def get_ptp_interface(self, ptp_host_if_name: str) -> PTPHostInterfaceSetup: + """ + Gets the PTP interface associated with the name ptp_host_if_name. + + Args: + ptp_host_if_name (str): Name of the ptp_host_if associated with this clock setup with the provided name. + + Returns: + PTPHostInterfaceSetup: PTPHostInterfaceSetup with the name passed in. + """ + for ptp_interface in self.ptp_interfaces: + if ptp_interface.get_name() == ptp_host_if_name: + return ptp_interface + raise Exception(f"There is no ptp host interface with the name {ptp_host_if_name} associated with the clock setup {self.get_name()}") diff --git a/keywords/ptp/setup/object/phc2sys_setup.py b/keywords/ptp/setup/object/phc2sys_setup.py new file mode 100644 index 00000000..06ef0011 --- /dev/null +++ b/keywords/ptp/setup/object/phc2sys_setup.py @@ -0,0 +1,86 @@ +from typing import Any, Dict, List + +from keywords.ptp.setup.object.ptp_host_interface_setup import PTPHostInterfaceSetup + + +class PHC2SysSetup: + """ + Class models a phc2sys setup + """ + + def __init__(self, setup_dict: Dict[str, Any], ptp_host_ifs_dict: Dict[str, PTPHostInterfaceSetup]): + """ + Constructor. + + Args: + setup_dict (Dict[str, Any]): The dictionary read from the JSON setup template file associated with this phc2sys setup. + ptp_host_ifs_dict (Dict[str, PTPHostInterfaceSetup]): The dictionary that maps the name of thePTPHostInterfaceSetup to its associated object. + + """ + if "name" not in setup_dict: + raise Exception("Every phc2sys entry should have a name.") + self.name = setup_dict["name"] + + if "instance_hostnames" not in setup_dict: + raise Exception(f"The phc2sys entry {self.name} must have instance_hostnames defined.") + self.instance_hostnames = setup_dict["instance_hostnames"] + + if "instance_parameters" not in setup_dict: + raise Exception(f"The phc2sys entry {self.name} must have instance_parameters defined.") + self.instance_parameters = setup_dict["instance_parameters"] + + if "ptp_interface_names" not in setup_dict: + raise Exception(f"The phc2sys entry {self.name} must have ptp_interface_names defined.") + + ptp_interfaces = [] + for ptp_interface_name in setup_dict["ptp_interface_names"]: + if ptp_interface_name not in ptp_host_ifs_dict: + raise Exception(f"The ptp_host_if entry {ptp_interface_name} must be defined.") + ptp_interfaces.append(ptp_host_ifs_dict[ptp_interface_name]) + self.ptp_interfaces = ptp_interfaces + + def __str__(self): + """ + String representation of this object. + + Returns: + str: String representation of this object. + + """ + return self.get_name() + + def get_name(self) -> str: + """ + Gets the name of this phc2sys setup. + + Returns: + str: The name of this phc2sys setup. + """ + return self.name + + def get_instance_hostnames(self) -> list[str]: + """ + Gets the list of instance hostnames. + + Returns: + list[str]: The list of instance hostnames. + """ + return self.instance_hostnames + + def get_instance_parameters(self) -> str: + """ + Gets the instance parameters as a string. + + Returns: + str: The instance parameters as a string + """ + return self.instance_parameters + + def get_ptp_interfaces(self) -> List[PTPHostInterfaceSetup]: + """ + Gets the list of PTP interfaces. + + Returns: + List[PTPHostInterfaceSetup]: The list of PTP interfaces. + """ + return self.ptp_interfaces diff --git a/keywords/ptp/setup/object/ptp4l_setup.py b/keywords/ptp/setup/object/ptp4l_setup.py new file mode 100644 index 00000000..16dd0c3e --- /dev/null +++ b/keywords/ptp/setup/object/ptp4l_setup.py @@ -0,0 +1,86 @@ +from typing import Any, Dict, List + +from keywords.ptp.setup.object.ptp_host_interface_setup import PTPHostInterfaceSetup + + +class PTP4LSetup: + """ + Class models a ptp4l setup + """ + + def __init__(self, setup_dict: Dict[str, Any], ptp_host_ifs_dict: Dict[str, PTPHostInterfaceSetup]): + """ + Constructor. + + Args: + setup_dict (Dict[str, Any]): The dictionary read from the JSON setup template file associated with this ptp4l setup. + ptp_host_ifs_dict (Dict[str, PTPHostInterfaceSetup]): The dictionary that maps the name of thePTPHostInterfaceSetup to its associated object. + + """ + if "name" not in setup_dict: + raise Exception("Every PTP4L entry should have a name.") + self.name = setup_dict["name"] + + if "instance_hostnames" not in setup_dict: + raise Exception(f"The ptp4l entry {self.name} must have instance_hostnames defined.") + self.instance_hostnames = setup_dict["instance_hostnames"] + + if "instance_parameters" not in setup_dict: + raise Exception(f"The ptp4l entry {self.name} must have instance_parameters defined.") + self.instance_parameters = setup_dict["instance_parameters"] + + if "ptp_interface_names" not in setup_dict: + raise Exception(f"The ptp4l entry {self.name} must have ptp_interface_names defined.") + + ptp_interfaces = [] + for ptp_interface_name in setup_dict["ptp_interface_names"]: + if ptp_interface_name not in ptp_host_ifs_dict: + raise Exception(f"The ptp_host_if entry {ptp_interface_name} must be defined.") + ptp_interfaces.append(ptp_host_ifs_dict[ptp_interface_name]) + self.ptp_interfaces = ptp_interfaces + + def __str__(self): + """ + String representation of this object. + + Returns: + str: String representation of this object. + + """ + return self.get_name() + + def get_name(self) -> str: + """ + Gets the name of this ptp4l setup. + + Returns: + str: The name of this ptp4l setup. + """ + return self.name + + def get_instance_hostnames(self) -> list[str]: + """ + Gets the list of instance hostnames. + + Returns: + list[str]: The list of instance hostnames. + """ + return self.instance_hostnames + + def get_instance_parameters(self) -> str: + """ + Gets the instance parameters as a string. + + Returns: + str: The instance parameters as a string + """ + return self.instance_parameters + + def get_ptp_interfaces(self) -> List[PTPHostInterfaceSetup]: + """ + Gets the list of PTP interfaces. + + Returns: + List[PTPHostInterfaceSetup]: The list of PTP interfaces. + """ + return self.ptp_interfaces diff --git a/keywords/ptp/setup/object/ptp_host_interface_setup.py b/keywords/ptp/setup/object/ptp_host_interface_setup.py new file mode 100644 index 00000000..a6190f7a --- /dev/null +++ b/keywords/ptp/setup/object/ptp_host_interface_setup.py @@ -0,0 +1,77 @@ +from typing import Any, Dict, List + + +class PTPHostInterfaceSetup: + """ + This class models a PTP Host interface setup. + """ + + def __init__(self, setup_dict: Dict[str, Any]): + """ + Constructor. + + Args: + setup_dict (Dict[str, Any]): The dictionary read from the JSON setup template file associated with this ptp host interface setup. + + """ + if "name" not in setup_dict: + raise Exception("Every ptp host interface entry should have a name.") + self.name = setup_dict["name"] + + if "ptp_interface_parameter" not in setup_dict: + raise Exception(f"The ptp host interface entry {self.name} must have ptp_interface_parameter defined.") + self.ptp_interface_parameter = setup_dict["ptp_interface_parameter"] + + if "controller_0_interfaces" not in setup_dict: + raise Exception(f"The ptp host interface entry {self.name} must have controller_0_interfaces defined.") + self.controller_0_interfaces = setup_dict["controller_0_interfaces"] + + if "controller_1_interfaces" not in setup_dict: + raise Exception(f"The ptp host interface entry {self.name} must have controller_1_interfaces defined.") + self.controller_0_interfaces = setup_dict["controller_1_interfaces"] + + def __str__(self): + """ + String representation of this object. + + Returns: + str: String representation of this object. + + """ + return self.get_name() + + def get_name(self) -> str: + """ + Gets the name of this ptp host interface setup. + + Returns: + str: The name of this ptp host interface setup. + """ + return self.name + + def get_ptp_interface_parameter(self) -> str: + """ + Gets the ptp_interface_parameter of this ptp host interface setup. + + Returns: + str: The ptp_interface_parameter of this ptp host interface setup. + """ + return self.ptp_interface_parameter + + def get_controller_0_interfaces(self) -> List[str]: + """ + Gets the controller_0_interfaces of this ptp host interface setup. + + Returns: + List[str]: The controller_0_interfaces of this ptp host interface setup. + """ + return self.controller_0_interfaces + + def get_controller_1_interfaces(self) -> List[str]: + """ + Gets the controller_1_interfaces of this ptp host interface setup. + + Returns: + List[str]: The controller_1_interfaces of this ptp host interface setup. + """ + return self.controller_1_interfaces diff --git a/keywords/ptp/setup/object/ptp_setup.py b/keywords/ptp/setup/object/ptp_setup.py new file mode 100644 index 00000000..ffd51e06 --- /dev/null +++ b/keywords/ptp/setup/object/ptp_setup.py @@ -0,0 +1,110 @@ +from typing import Dict, List + +from keywords.ptp.setup.object.clock_setup import ClockSetup +from keywords.ptp.setup.object.phc2sys_setup import PHC2SysSetup +from keywords.ptp.setup.object.ptp4l_setup import PTP4LSetup +from keywords.ptp.setup.object.ptp_host_interface_setup import PTPHostInterfaceSetup +from keywords.ptp.setup.object.ts2phc_setup import TS2PHCSetup + + +class PTPSetup: + """ + This class represents a PTP Setup. + """ + + def __init__(self, setup_dict: Dict[str, Dict]): + """ + Constructor. + + Args: + setup_dict (Dict[str, Dict]): The dictionary read from the JSON config file associated with this ptp_setup. + + """ + self.ptp4l_setup_list: List[PTP4LSetup] = [] + self.phc2sys_setup_list: List[PHC2SysSetup] = [] + self.ts2phc_setup_list: List[TS2PHCSetup] = [] + self.clock_setup_list: List[ClockSetup] = [] + self.host_ptp_if_dict: Dict[str, PTPHostInterfaceSetup] = {} # Name -> PTPHostInterfaceSetup + + if "ptp_instances" not in setup_dict: + raise Exception("You must define a ptp_instances section in your ptp setup_dict") + + if "ptp_host_ifs" not in setup_dict: + raise Exception("You must define a ptp_host_ifs section in your ptp setup_dict") + + ptp_host_ifs = setup_dict["ptp_host_ifs"] + for ptp_host_if in ptp_host_ifs: + ptp_host_if_object = PTPHostInterfaceSetup(ptp_host_if) + ptp_host_if_name = ptp_host_if_object.get_name() + self.host_ptp_if_dict[ptp_host_if_name] = ptp_host_if_object + + ptp_instances_dict = setup_dict["ptp_instances"] + if "ptp4l" in ptp_instances_dict: + ptp4l_list = ptp_instances_dict["ptp4l"] + for ptp4l_entry_dict in ptp4l_list: + ptp4l_setup = PTP4LSetup(ptp4l_entry_dict, self.host_ptp_if_dict) + self.ptp4l_setup_list.append(ptp4l_setup) + + if "phc2sys" in ptp_instances_dict: + phc2sys_list = ptp_instances_dict["phc2sys"] + for phc2sys_entry_dict in phc2sys_list: + phc2sys_setup = PHC2SysSetup(phc2sys_entry_dict, self.host_ptp_if_dict) + self.phc2sys_setup_list.append(phc2sys_setup) + + if "ts2phc" in ptp_instances_dict: + ts2phc_list = ptp_instances_dict["ts2phc"] + for ts2phc_entry_dict in ts2phc_list: + ts2phc_setup = TS2PHCSetup(ts2phc_entry_dict, self.host_ptp_if_dict) + self.ts2phc_setup_list.append(ts2phc_setup) + + if "clock" in ptp_instances_dict: + clock_list = ptp_instances_dict["clock"] + for clock_entry_dict in clock_list: + clock_setup = ClockSetup(clock_entry_dict, self.host_ptp_if_dict) + self.clock_setup_list.append(clock_setup) + + def __str__(self): + """ + String representation of this object. + + Returns: + str: String representation of this object. + + """ + return "PTPSetup" + + def get_ptp4l_setup_list(self) -> List[PTP4LSetup]: + """ + Getter for the list of ptp4l setups. + + Returns: + List[PTP4LSetup]: list of ptp4l setups + """ + return self.ptp4l_setup_list + + def get_phc2sys_setup_list(self) -> List[PHC2SysSetup]: + """ + Getter for the list of phc2sys setups. + + Returns: + List[PHC2SysSetup]: list of phc2sys setups + """ + return self.phc2sys_setup_list + + def get_ts2phc_setup_list(self) -> List[TS2PHCSetup]: + """ + Getter for the list of ts2phc setups. + + Returns: + List[TS2PHCSetup]: list of ts2phc setups + """ + return self.ts2phc_setup_list + + def get_clock_setup_list(self) -> List[ClockSetup]: + """ + Getter for the list of clock setups. + + Returns: + List[ClockSetup]: list of clock setups + """ + return self.clock_setup_list diff --git a/keywords/ptp/setup/object/ts2phc_setup.py b/keywords/ptp/setup/object/ts2phc_setup.py new file mode 100644 index 00000000..f4c773d1 --- /dev/null +++ b/keywords/ptp/setup/object/ts2phc_setup.py @@ -0,0 +1,86 @@ +from typing import Any, Dict, List + +from keywords.ptp.setup.object.ptp_host_interface_setup import PTPHostInterfaceSetup + + +class TS2PHCSetup: + """ + Class models a ts2phc setup + """ + + def __init__(self, setup_dict: Dict[str, Any], ptp_host_ifs_dict: Dict[str, PTPHostInterfaceSetup]): + """ + Constructor. + + Args: + setup_dict (Dict[str, Any]): The dictionary read from the JSON setup template file associated with this ts2phc setup. + ptp_host_ifs_dict (Dict[str, PTPHostInterfaceSetup]): The dictionary that maps the name of thePTPHostInterfaceSetup to its associated object. + + """ + if "name" not in setup_dict: + raise Exception("Every ts2phc entry should have a name.") + self.name = setup_dict["name"] + + if "instance_hostnames" not in setup_dict: + raise Exception(f"The ts2phc entry {self.name} must have instance_hostnames defined.") + self.instance_hostnames = setup_dict["instance_hostnames"] + + if "instance_parameters" not in setup_dict: + raise Exception(f"The ts2phc entry {self.name} must have instance_parameters defined.") + self.instance_parameters = setup_dict["instance_parameters"] + + if "ptp_interface_names" not in setup_dict: + raise Exception(f"The ts2phc entry {self.name} must have ptp_interface_names defined.") + + ptp_interfaces = [] + for ptp_interface_name in setup_dict["ptp_interface_names"]: + if ptp_interface_name not in ptp_host_ifs_dict: + raise Exception(f"The ptp_host_if entry {ptp_interface_name} must be defined.") + ptp_interfaces.append(ptp_host_ifs_dict[ptp_interface_name]) + self.ptp_interfaces = ptp_interfaces + + def __str__(self): + """ + String representation of this object. + + Returns: + str: String representation of this object. + + """ + return self.get_name() + + def get_name(self) -> str: + """ + Gets the name of this ts2phc setup. + + Returns: + str: The name of this ts2phc setup. + """ + return self.name + + def get_instance_hostnames(self) -> list[str]: + """ + Gets the list of instance hostnames. + + Returns: + list[str]: The list of instance hostnames. + """ + return self.instance_hostnames + + def get_instance_parameters(self) -> str: + """ + Gets the instance parameters as a string. + + Returns: + str: The instance parameters as a string + """ + return self.instance_parameters + + def get_ptp_interfaces(self) -> List[PTPHostInterfaceSetup]: + """ + Gets the list of PTP interfaces. + + Returns: + List[PTPHostInterfaceSetup]: The list of PTP interfaces. + """ + return self.ptp_interfaces diff --git a/keywords/ptp/setup/ptp_setup_reader.py b/keywords/ptp/setup/ptp_setup_reader.py new file mode 100644 index 00000000..b02f6fc5 --- /dev/null +++ b/keywords/ptp/setup/ptp_setup_reader.py @@ -0,0 +1,40 @@ +import json5 +from jinja2 import Template + +from config.configuration_manager import ConfigurationManager +from keywords.base_keyword import BaseKeyword +from keywords.ptp.setup.object.ptp_setup import PTPSetup + + +class PTPSetupKeywords(BaseKeyword): + """ + This class is responsible for reading ptp_setup files and matching them with the ptp config. + """ + + def generate_ptp_setup_from_template(self, template_file_location: str) -> PTPSetup: + """ + This function will read the template_file specified and will replace values based on the ptp_config. + + Args: + template_file_location (str): Path in the repo to the Template JSON5 file. e.g. 'resources/ptp/setup/ptp_setup_template.json5' + + Returns: + PTPSetup: An object representing the setup. + + """ + # Load the Template JSON5 file. + with open(template_file_location, "r") as template_file: + json5_template = template_file.read() + + # Build a replacement dictionary from the PTP Config + ptp_config = ConfigurationManager.get_ptp_config() + replacement_dictionary = ptp_config.get_all_hosts_dictionary() + + # Render the JSON5 file by replacing the tokens. + template = Template(json5_template) + rendered_json_string = template.render(replacement_dictionary) + json_data = json5.loads(rendered_json_string) + + # Turn the JSON Data into a ptp_setup object. + ptp_setup = PTPSetup(json_data) + return ptp_setup diff --git a/resources/ptp/setup/ptp_setup_template.json5 b/resources/ptp/setup/ptp_setup_template.json5 new file mode 100644 index 00000000..50f86d76 --- /dev/null +++ b/resources/ptp/setup/ptp_setup_template.json5 @@ -0,0 +1,225 @@ +{ + ptp_instances: { + + ptp4l: [ + + { + name: "ptp1", + instance_hostnames : ["controller_1", "controller_0"] , + instance_parameters: "tx_timestamp_timeout=700 domainNumber=24 dataset_comparison=G.8275.x priority2=100 boundary_clock_jbod=1", + ptp_interface_names: [ + "ptp1if1", + "ptp1if2", + ] + }, + + { + name: "ptp2", + instance_hostnames : [], + instance_parameters: "dataset_comparison=G.8275.x domainNumber=24 tx_timestamp_timeout=700 boundary_clock_jbod=1 priority2=110", + ptp_interface_names: [ + "ptp2if1", + "ptp2if2" + ], + }, + + { + 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", + ptp_interface_names: [ + "ptp3if1", + "ptp3if2" + ], + }, + + { + 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", + ptp_interface_names: [ + "ptp4if1", + "ptp4if2" + ], + } + + ], + + phc2sys : [ + + { + name: "phc1", + instance_hostnames : ["controller_1", "controller_0"], + instance_parameters: "cmdline_opts=-s {{controller_0.nic1.conn_to_sprint}} -O -37 -m", // lab_topology_synce3.json5 : values is enp81s0f2 + ptp_interface_names: [ + "phc1if1", + ], + }, + + { + name: "phc2", + instance_hostnames : [], + instance_parameters: "uds_address=/var/run/ptp4l-ptp2 domainNumber=24", + ptp_interface_names: [ + "phc2if1", + ], + }, + + { + name: "phc3", + instance_hostnames : [], + instance_parameters: "uds_address=/var/run/ptp4l-ptp3 domainNumber=24", + ptp_interface_names: [ + "phc3if1", + ], + }, + + { + name: "phc4", + instance_hostnames : [], + instance_parameters: "uds_address=/var/run/ptp4l-ptp4 domainNumber=24", + ptp_interface_names: [ + "phc4if1", + ], + }, + + ], + + ts2phc : [ + + { + name: "ts1", + instance_hostnames : ["controller_0"], + instance_parameters: "ts2phc.nmea_serialport=/dev/gnss0", + ptp_interface_names: [ + "ts1if1", + ], + } + + ], + + clock : [ + + { + name: "clock1", + instance_hostnames : ["controller_0"], + instance_parameters: "", + ptp_interface_names: [ + "clock1if1", + "clock1if2", + ], + } + + ], + + }, + + ptp_host_ifs: [ + + { + name: "ptp1if1", + controller_0_interfaces: ["{{ controller_0.nic1.conn_to_ctrl1_nic1 }}"], + controller_1_interfaces: ["{{ controller_1.nic1.conn_to_ctrl0_nic1 }}"], + ptp_interface_parameter : "", + }, + + { + name: "ptp1if2", + controller_0_interfaces: ["{{ controller_0.nic1.conn_to_spirent }}"], + controller_1_interfaces: ["{{ controller_1.nic1.conn_to_spirent }}"], + ptp_interface_parameter : "", + }, + + { + name: "ptp2if1", + controller_0_interfaces: [], + controller_1_interfaces: [], + ptp_interface_parameter : "", + }, + + { + name: "ptp2if2", + controller_0_interfaces: [], + controller_1_interfaces: [], + ptp_interface_parameter : "", + }, + + { + name: "ptp3if1", + controller_0_interfaces: ["{{ controller_0.nic2.conn_to_ctrl1_nic2 }}"], + controller_1_interfaces: [], + ptp_interface_parameter : "", + }, + + { + name: "ptp3if2", + controller_0_interfaces: ["{{ controller_0.nic2.conn_to_spirent }}"], + controller_1_interfaces: [], + ptp_interface_parameter : "", + }, + + { + name: "ptp4if1", + controller_0_interfaces: [], + controller_1_interfaces: ["{{ controller_0.nic2.conn_to_ctrl1_nic2 }}"], + ptp_interface_parameter : "", + }, + + { + name: "ptp4if2", + controller_0_interfaces: [], + controller_1_interfaces: ["{{ controller_1.nic2.conn_to_spirent }}"], + ptp_interface_parameter : "", + }, + + { + name: "phc1if1", + controller_0_interfaces: [], + controller_1_interfaces: [], + ptp_interface_parameter : "", + }, + + { + name: "phc2if1", + controller_0_interfaces: [], + controller_1_interfaces: [], + ptp_interface_parameter : "", + }, + + { + name: "phc3if1", + controller_0_interfaces: [], + controller_1_interfaces: [], + ptp_interface_parameter : "", + }, + + { + name: "phc4if1", + controller_0_interfaces: [], + controller_1_interfaces: ["{{ controller_1.nic1.base_port }}", "{{ controller_1.nic2.base_port }}"], + ptp_interface_parameter :"", + }, + + { + name: "ts1if1", + controller_0_interfaces: ["{{ controller_0.nic1.conn_to_spirent }}", "{{ controller_0.nic2.base_port }}"], + controller_1_interfaces: [], + ptp_interface_parameter : "", + }, + + { + name: "clock1if1", + controller_0_interfaces: ["{{ controller_0.nic1.conn_to_spirent }}"], + controller_1_interfaces: [], + ptp_interface_parameter : "{{ controller_0.nic1.sma1_to_nic2 }}", + }, + + { + name: "clock1if2", + controller_0_interfaces: ["{{ controller_0.nic2.base_port }}"], + controller_1_interfaces: [], + ptp_interface_parameter : "{{ controller_0.nic2.sma1_to_nic1 }}", + }, + ], + +} diff --git a/unit_tests/config/ptp/ptp_config_test.py b/unit_tests/config/ptp/ptp_config_test.py index efb3b056..eb2f55a4 100644 --- a/unit_tests/config/ptp/ptp_config_test.py +++ b/unit_tests/config/ptp/ptp_config_test.py @@ -1,4 +1,6 @@ -from config.configuration_file_locations_manager import ConfigurationFileLocationsManager +from config.configuration_file_locations_manager import ( + ConfigurationFileLocationsManager, +) from config.configuration_manager import ConfigurationManagerClass @@ -20,3 +22,4 @@ def test_default_ptp_config(): first_nic = config_for_controller_0.get_nic("nic1") assert first_nic.get_sma1_to_nic2() == "output" + assert first_nic.get_conn_to_ctrl1_nic1() == "enp81s0f1" diff --git a/unit_tests/keyword/ptp/ptp_setup_reader_test.py b/unit_tests/keyword/ptp/ptp_setup_reader_test.py new file mode 100644 index 00000000..2450651d --- /dev/null +++ b/unit_tests/keyword/ptp/ptp_setup_reader_test.py @@ -0,0 +1,32 @@ +from config.configuration_file_locations_manager import ( + ConfigurationFileLocationsManager, +) +from config.configuration_manager import ConfigurationManager +from framework.resources.resource_finder import get_stx_resource_path +from keywords.ptp.setup.ptp_setup_reader import PTPSetupKeywords + + +def test_generate_ptp_setup_from_template(): + """ + Tests that the generation of a PTPSetup from a Config and Template works as expected. + + """ + config_file_locations = ConfigurationFileLocationsManager() + ConfigurationManager.load_configs(config_file_locations) + + ptp_setup_template_path = get_stx_resource_path("resources/ptp/setup/ptp_setup_template.json5") + ptp_setup_keywords = PTPSetupKeywords() + ptp_setup = ptp_setup_keywords.generate_ptp_setup_from_template(ptp_setup_template_path) + + ptp4l_setup_list = ptp_setup.get_ptp4l_setup_list() + assert len(ptp4l_setup_list) == 4 + phc2sys_setup_list = ptp_setup.get_phc2sys_setup_list() + assert len(phc2sys_setup_list) == 4 + ts2phc_setup_list = ptp_setup.get_ts2phc_setup_list() + assert len(ts2phc_setup_list) == 1 + + clock_setup_list = ptp_setup.get_clock_setup_list() + assert len(clock_setup_list) == 1 + clock1 = clock_setup_list[0] + clock1_interfaces = clock1.get_ptp_interfaces() + assert len(clock1_interfaces) == 2