Refactoring
Add new keywords to support the software deploy precheck testing. Precheck timeout created Jira: CGTS-74904 Tested On: WRCP-SAPPHIRE-RAPIDS-8 YOW-WRCP-STD-017 Automation Logs: /sharedsan/AUTOMATION_LOGS/WRCP/precheck/WRCP-SAPPHIRE-RAPIDS-8/202511261708/full_logs.txt /sharedsan/AUTOMATION_LOGS/WRCP/precheck/YOW-WRCP-STD-017/202511261704/full_logs.txt Change-Id: Ib2b54e5feda122617830fbaad2c083923a5825e5 Signed-off-by: Elson Oliveira <eclaudio@windriver.com>
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
"upload_poll_interval_sec": 30,
|
||||
"deployment_timeout_sec": 7200,
|
||||
"activation_timeout_sec": 3600,
|
||||
"precheck_timeout_sec": 300,
|
||||
|
||||
// Remote copy settings
|
||||
"copy_from_remote": true,
|
||||
|
||||
@@ -46,6 +46,7 @@ class USMConfig:
|
||||
self.upload_poll_interval_sec = usm_dict.get("upload_poll_interval_sec", 30)
|
||||
self.upload_patch_timeout_sec = usm_dict.get("upload_patch_timeout_sec", 1800)
|
||||
self.upload_release_timeout_sec = usm_dict.get("upload_release_timeout_sec", 1800)
|
||||
self.precheck_timeout_sec = usm_dict.get("precheck_timeout_sec", 300)
|
||||
|
||||
def validate_config(self) -> None:
|
||||
"""
|
||||
@@ -426,3 +427,19 @@ class USMConfig:
|
||||
value (int): Maximum seconds to wait for release upload to complete.
|
||||
"""
|
||||
self.upload_release_timeout_sec = value
|
||||
|
||||
def get_precheck_timeout_sec(self) -> int:
|
||||
"""Get timeout duration for deploy precheck completion.
|
||||
|
||||
Returns:
|
||||
int: Maximum seconds to wait for deploy precheck to complete.
|
||||
"""
|
||||
return self.precheck_timeout_sec
|
||||
|
||||
def set_precheck_timeout_sec(self, value: int) -> None:
|
||||
"""Set timeout duration for release upload completion.
|
||||
|
||||
Args:
|
||||
value (int): Maximum seconds to wait for release upload to complete.
|
||||
"""
|
||||
self.precheck_timeout_sec = value
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import json5
|
||||
|
||||
from framework.exceptions.keyword_exception import KeywordException
|
||||
from framework.logging.automation_logger import get_logger
|
||||
from framework.rest.rest_response import RestResponse
|
||||
from keywords.cloud_platform.system.host.objects.host_capabilities_object import HostCapabilities
|
||||
from keywords.cloud_platform.system.host.objects.system_host_show_object import SystemHostShowObject
|
||||
@@ -13,203 +13,209 @@ class SystemHostShowOutput:
|
||||
This class parses the output of 'system host-show' command into an object of type SystemHostShowObject.
|
||||
"""
|
||||
|
||||
def __init__(self, system_host_show_output):
|
||||
def __init__(self, system_host_show_output: str):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
Args:
|
||||
system_host_show_output: output of 'system host-show' command as a list of strings.
|
||||
system_host_show_output (str): output of 'system host-show' command as a list of strings.
|
||||
"""
|
||||
|
||||
if isinstance(system_host_show_output, RestResponse): # came from REST and is already in dict form
|
||||
json_object = system_host_show_output.get_json_content()
|
||||
if 'ihosts' in json_object:
|
||||
hosts = json_object['ihosts']
|
||||
if "ihosts" in json_object:
|
||||
hosts = json_object["ihosts"]
|
||||
else:
|
||||
hosts = [json_object]
|
||||
else: # this came from a system command and must be parsed
|
||||
else: # this came from a system command and must be parsed
|
||||
system_vertical_table_parser = SystemVerticalTableParser(system_host_show_output)
|
||||
output_values = system_vertical_table_parser.get_output_values_dict()
|
||||
output_values = system_host_show_output
|
||||
hosts = [output_values]
|
||||
|
||||
|
||||
self.system_host_show_objects: list[SystemHostShowObject] = []
|
||||
for host in hosts:
|
||||
system_host_show_object = SystemHostShowObject()
|
||||
|
||||
if 'action' in host:
|
||||
system_host_show_object.set_action(host['action'])
|
||||
if "action" in host:
|
||||
system_host_show_object.set_action(host["action"])
|
||||
|
||||
if 'administrative' in host:
|
||||
system_host_show_object.set_administrative(host['administrative'])
|
||||
if "administrative" in host:
|
||||
system_host_show_object.set_administrative(host["administrative"])
|
||||
|
||||
if 'apparmor' in host:
|
||||
system_host_show_object.set_apparmor(host['apparmor'])
|
||||
if "apparmor" in host:
|
||||
system_host_show_object.set_apparmor(host["apparmor"])
|
||||
|
||||
if 'availability' in host:
|
||||
system_host_show_object.set_availability(host['availability'])
|
||||
if "availability" in host:
|
||||
system_host_show_object.set_availability(host["availability"])
|
||||
|
||||
if 'bm_ip' in host:
|
||||
system_host_show_object.set_bm_ip(host['bm_ip'])
|
||||
if "bm_ip" in host:
|
||||
system_host_show_object.set_bm_ip(host["bm_ip"])
|
||||
|
||||
if 'bm_type' in host:
|
||||
system_host_show_object.set_bm_type(host['bm_type'])
|
||||
if "bm_type" in host:
|
||||
system_host_show_object.set_bm_type(host["bm_type"])
|
||||
|
||||
if 'bm_username' in host:
|
||||
system_host_show_object.set_bm_username(host['bm_username'])
|
||||
if "bm_username" in host:
|
||||
system_host_show_object.set_bm_username(host["bm_username"])
|
||||
|
||||
if 'boot_device' in host:
|
||||
system_host_show_object.set_boot_device(host['boot_device'])
|
||||
if "boot_device" in host:
|
||||
system_host_show_object.set_boot_device(host["boot_device"])
|
||||
|
||||
if 'capabilities' in host:
|
||||
capabilities_dict = host['capabilities']
|
||||
if "capabilities" in host:
|
||||
capabilities_dict = host["capabilities"]
|
||||
# if this from system, we need to parse the string to a dict
|
||||
if not isinstance(system_host_show_output, RestResponse):
|
||||
capabilities_dict = json5.loads(host['capabilities'])
|
||||
capabilities_dict = json5.loads(host["capabilities"])
|
||||
|
||||
capabilities = HostCapabilities()
|
||||
if 'is_max_cpu_configurable' in capabilities_dict:
|
||||
capabilities.set_is_max_cpu_configurable(capabilities_dict['is_max_cpu_configurable'])
|
||||
if 'mgmt_ipsec' in capabilities_dict:
|
||||
capabilities.set_mgmt_ipsec(capabilities_dict['mgmt_ipsec'])
|
||||
if 'stor_function' in capabilities_dict:
|
||||
capabilities.set_stor_function(capabilities_dict['stor_function'])
|
||||
if 'Personality' in capabilities_dict:
|
||||
capabilities.set_personality(capabilities_dict['Personality'])
|
||||
if "is_max_cpu_configurable" in capabilities_dict:
|
||||
capabilities.set_is_max_cpu_configurable(capabilities_dict["is_max_cpu_configurable"])
|
||||
if "mgmt_ipsec" in capabilities_dict:
|
||||
capabilities.set_mgmt_ipsec(capabilities_dict["mgmt_ipsec"])
|
||||
if "stor_function" in capabilities_dict:
|
||||
capabilities.set_stor_function(capabilities_dict["stor_function"])
|
||||
if "Personality" in capabilities_dict:
|
||||
capabilities.set_personality(capabilities_dict["Personality"])
|
||||
system_host_show_object.set_capabilities(capabilities)
|
||||
|
||||
if 'clock_synchronization' in host:
|
||||
system_host_show_object.set_clock_synchronization(host['clock_synchronization'])
|
||||
if "clock_synchronization" in host:
|
||||
system_host_show_object.set_clock_synchronization(host["clock_synchronization"])
|
||||
|
||||
if 'config_applied' in host:
|
||||
system_host_show_object.set_config_applied(host['config_applied'])
|
||||
if "config_applied" in host:
|
||||
system_host_show_object.set_config_applied(host["config_applied"])
|
||||
|
||||
if 'config_status' in host:
|
||||
system_host_show_object.set_config_status(host['config_status'])
|
||||
if "config_status" in host:
|
||||
system_host_show_object.set_config_status(host["config_status"])
|
||||
|
||||
if 'config_target' in host:
|
||||
system_host_show_object.set_config_target(host['config_target'])
|
||||
if "config_target" in host:
|
||||
system_host_show_object.set_config_target(host["config_target"])
|
||||
|
||||
if 'console' in host:
|
||||
system_host_show_object.set_console(host['console'])
|
||||
if "console" in host:
|
||||
system_host_show_object.set_console(host["console"])
|
||||
|
||||
if 'created_at' in host:
|
||||
system_host_show_object.set_created_at(host['created_at'])
|
||||
if "created_at" in host:
|
||||
system_host_show_object.set_created_at(host["created_at"])
|
||||
|
||||
if 'cstates_available' in host:
|
||||
system_host_show_object.set_cstates_available(host['cstates_available'])
|
||||
if "cstates_available" in host:
|
||||
system_host_show_object.set_cstates_available(host["cstates_available"])
|
||||
|
||||
if 'device_image_update' in host:
|
||||
system_host_show_object.set_device_image_update(host['device_image_update'])
|
||||
if "device_image_update" in host:
|
||||
system_host_show_object.set_device_image_update(host["device_image_update"])
|
||||
|
||||
if 'hostname' in host:
|
||||
system_host_show_object.set_hostname(host['hostname'])
|
||||
if "hostname" in host:
|
||||
system_host_show_object.set_hostname(host["hostname"])
|
||||
|
||||
if 'hw_settle' in host:
|
||||
system_host_show_object.set_hw_settle(int(host['hw_settle']))
|
||||
if "hw_settle" in host:
|
||||
system_host_show_object.set_hw_settle(int(host["hw_settle"]))
|
||||
|
||||
if 'id' in host:
|
||||
system_host_show_object.set_id(int(host['id']))
|
||||
if "id" in host:
|
||||
system_host_show_object.set_id(int(host["id"]))
|
||||
|
||||
if 'install_output' in host:
|
||||
system_host_show_object.set_install_output(host['install_output'])
|
||||
if "install_output" in host:
|
||||
system_host_show_object.set_install_output(host["install_output"])
|
||||
|
||||
if 'install_state' in host:
|
||||
system_host_show_object.set_install_state(host['install_state'])
|
||||
if "install_state" in host:
|
||||
system_host_show_object.set_install_state(host["install_state"])
|
||||
|
||||
if 'install_state_info' in host:
|
||||
system_host_show_object.set_install_state_info(host['install_state_info'])
|
||||
if "install_state_info" in host:
|
||||
system_host_show_object.set_install_state_info(host["install_state_info"])
|
||||
|
||||
if 'inv_state' in host:
|
||||
system_host_show_object.set_inv_state(host['inv_state'])
|
||||
if "inv_state" in host:
|
||||
system_host_show_object.set_inv_state(host["inv_state"])
|
||||
|
||||
if 'invprovision' in host:
|
||||
system_host_show_object.set_invprovision(host['invprovision'])
|
||||
if "invprovision" in host:
|
||||
system_host_show_object.set_invprovision(host["invprovision"])
|
||||
|
||||
if 'iscsi_initiator_name' in host:
|
||||
system_host_show_object.set_iscsi_initiator_name(host['iscsi_initiator_name'])
|
||||
if "iscsi_initiator_name" in host:
|
||||
system_host_show_object.set_iscsi_initiator_name(host["iscsi_initiator_name"])
|
||||
|
||||
if 'location' in host:
|
||||
system_host_show_object.set_location(host['location'])
|
||||
if "location" in host:
|
||||
system_host_show_object.set_location(host["location"])
|
||||
|
||||
if 'max_cpu_mhz_allowed' in host:
|
||||
system_host_show_object.set_max_cpu_mhz_allowed(host['max_cpu_mhz_allowed'])
|
||||
if "max_cpu_mhz_allowed" in host:
|
||||
system_host_show_object.set_max_cpu_mhz_allowed(host["max_cpu_mhz_allowed"])
|
||||
|
||||
if 'max_cpu_mhz_configured' in host:
|
||||
system_host_show_object.set_max_cpu_mhz_configured(host['max_cpu_mhz_configured'])
|
||||
if "max_cpu_mhz_configured" in host:
|
||||
system_host_show_object.set_max_cpu_mhz_configured(host["max_cpu_mhz_configured"])
|
||||
|
||||
if 'mgmt_mac' in host:
|
||||
system_host_show_object.set_mgmt_mac(host['mgmt_mac'])
|
||||
if "mgmt_mac" in host:
|
||||
system_host_show_object.set_mgmt_mac(host["mgmt_mac"])
|
||||
|
||||
if 'min_cpu_mhz_allowed' in host:
|
||||
system_host_show_object.set_min_cpu_mhz_allowed(host['min_cpu_mhz_allowed'])
|
||||
if "min_cpu_mhz_allowed" in host:
|
||||
system_host_show_object.set_min_cpu_mhz_allowed(host["min_cpu_mhz_allowed"])
|
||||
|
||||
if 'nvme_host_id' in host:
|
||||
system_host_show_object.set_nvme_host_id(host['nvme_host_id'])
|
||||
if "nvme_host_id" in host:
|
||||
system_host_show_object.set_nvme_host_id(host["nvme_host_id"])
|
||||
|
||||
if 'nvme_host_nqn' in host:
|
||||
system_host_show_object.set_nvme_host_nqn(host['nvme_host_nqn'])
|
||||
if "nvme_host_nqn" in host:
|
||||
system_host_show_object.set_nvme_host_nqn(host["nvme_host_nqn"])
|
||||
|
||||
if 'operational' in host:
|
||||
system_host_show_object.set_operational(host['operational'])
|
||||
if "operational" in host:
|
||||
system_host_show_object.set_operational(host["operational"])
|
||||
|
||||
if 'personality' in host:
|
||||
system_host_show_object.set_personality(host['personality'])
|
||||
if "personality" in host:
|
||||
system_host_show_object.set_personality(host["personality"])
|
||||
|
||||
if 'reboot_needed' in host:
|
||||
value = host['reboot_needed'] if isinstance(host['reboot_needed'], bool) else TypeConverter.str_to_bool(host['reboot_needed'])
|
||||
if "reboot_needed" in host:
|
||||
value = host["reboot_needed"] if isinstance(host["reboot_needed"], bool) else TypeConverter.str_to_bool(host["reboot_needed"])
|
||||
system_host_show_object.set_reboot_needed(value)
|
||||
|
||||
if 'reserved' in host:
|
||||
value = host['reserved'] if isinstance(host['reserved'], bool) else TypeConverter.str_to_bool(host['reserved'])
|
||||
if "reserved" in host:
|
||||
value = host["reserved"] if isinstance(host["reserved"], bool) else TypeConverter.str_to_bool(host["reserved"])
|
||||
system_host_show_object.set_reserved(value)
|
||||
|
||||
if 'rootfs_device' in host:
|
||||
system_host_show_object.set_rootfs_device(host['rootfs_device'])
|
||||
if "rootfs_device" in host:
|
||||
system_host_show_object.set_rootfs_device(host["rootfs_device"])
|
||||
|
||||
if 'serialid' in host:
|
||||
system_host_show_object.set_serialid(host['serialid'])
|
||||
if "serialid" in host:
|
||||
system_host_show_object.set_serialid(host["serialid"])
|
||||
|
||||
if 'software_load' in host:
|
||||
system_host_show_object.set_software_load(host['software_load'])
|
||||
if "software_load" in host:
|
||||
system_host_show_object.set_software_load(host["software_load"])
|
||||
|
||||
if 'subfunction_avail' in host:
|
||||
system_host_show_object.set_subfunction_avail(host['subfunction_avail'])
|
||||
if "subfunction_avail" in host:
|
||||
system_host_show_object.set_subfunction_avail(host["subfunction_avail"])
|
||||
|
||||
if 'subfunction_oper' in host:
|
||||
system_host_show_object.set_subfunction_oper(host['subfunction_oper'])
|
||||
if "subfunction_oper" in host:
|
||||
system_host_show_object.set_subfunction_oper(host["subfunction_oper"])
|
||||
|
||||
if 'subfunctions' in host:
|
||||
system_host_show_object.set_subfunctions(TypeConverter.parse_string_to_list(host['subfunctions']))
|
||||
if "subfunctions" in host:
|
||||
system_host_show_object.set_subfunctions(TypeConverter.parse_string_to_list(host["subfunctions"]))
|
||||
|
||||
if 'sw_version' in host:
|
||||
system_host_show_object.set_sw_version(host['sw_version'])
|
||||
if "sw_version" in host:
|
||||
system_host_show_object.set_sw_version(host["sw_version"])
|
||||
|
||||
if 'task' in host:
|
||||
system_host_show_object.set_task(host['task'])
|
||||
if "task" in host:
|
||||
system_host_show_object.set_task(host["task"])
|
||||
|
||||
if 'tboot' in host:
|
||||
system_host_show_object.set_tboot(host['tboot'])
|
||||
if "tboot" in host:
|
||||
system_host_show_object.set_tboot(host["tboot"])
|
||||
|
||||
if 'ttys_dcd' in host:
|
||||
value = host['ttys_dcd'] if isinstance(host['ttys_dcd'], bool) else TypeConverter.str_to_bool(host['ttys_dcd'])
|
||||
if "ttys_dcd" in host:
|
||||
value = host["ttys_dcd"] if isinstance(host["ttys_dcd"], bool) else TypeConverter.str_to_bool(host["ttys_dcd"])
|
||||
system_host_show_object.set_ttys_dcd(value)
|
||||
|
||||
if 'updated_at' in host:
|
||||
system_host_show_object.set_updated_at(host['updated_at'])
|
||||
if "updated_at" in host:
|
||||
system_host_show_object.set_updated_at(host["updated_at"])
|
||||
|
||||
if 'uptime' in host:
|
||||
system_host_show_object.set_uptime(host['uptime'])
|
||||
if "uptime" in host:
|
||||
system_host_show_object.set_uptime(host["uptime"])
|
||||
|
||||
if 'uuid' in host:
|
||||
system_host_show_object.set_uuid(host['uuid'])
|
||||
if "uuid" in host:
|
||||
system_host_show_object.set_uuid(host["uuid"])
|
||||
|
||||
if 'vim_progress_status' in host:
|
||||
system_host_show_object.set_vim_progress_status(host['vim_progress_status'])
|
||||
if "vim_progress_status" in host:
|
||||
system_host_show_object.set_vim_progress_status(host["vim_progress_status"])
|
||||
self.system_host_show_objects.append(system_host_show_object)
|
||||
|
||||
def _get_host_value(self, hostname: str=None):
|
||||
|
||||
def _get_host_value(self, hostname: str = None) -> SystemHostShowObject:
|
||||
"""
|
||||
This function will return a SystemHostShowObject of a specific hostname
|
||||
|
||||
Args:
|
||||
hostname (str): Host name to get its bmc type.
|
||||
|
||||
Returns:
|
||||
SystemHostShowObject: SystemHostShowObject Object of a hostname if specified
|
||||
"""
|
||||
if hostname:
|
||||
hosts = list(filter(lambda system_show_object: system_show_object.get_hostname() == hostname, self.system_host_show_objects))
|
||||
if hosts:
|
||||
@@ -222,69 +228,78 @@ class SystemHostShowOutput:
|
||||
raise KeywordException("There was not exactly 1 host")
|
||||
else:
|
||||
# return the first one
|
||||
return self.system_host_show_objects[0]
|
||||
return self.system_host_show_objects[0]
|
||||
|
||||
|
||||
def has_host_bmc_ipmi(self, hostname: str=None) -> bool:
|
||||
def has_host_bmc_ipmi(self, hostname: str = None) -> bool:
|
||||
"""
|
||||
This function will return True if bm_type of this host is 'ipmi'.
|
||||
|
||||
Returns: True if bm_type of this host is 'ipmi', False otherwise.
|
||||
Args:
|
||||
hostname (str): Host name to get its bmc type.
|
||||
|
||||
Returns:
|
||||
bool: True if bm_type of this host is 'ipmi', False otherwise.
|
||||
"""
|
||||
system_host_show_object = self._get_host_value(hostname)
|
||||
return system_host_show_object.get_bm_type() == "ipmi"
|
||||
|
||||
def has_host_bmc_redfish(self, hostname: str=None) -> bool:
|
||||
def has_host_bmc_redfish(self, hostname: str = None) -> bool:
|
||||
"""
|
||||
This function will return True if bm_type of this host is 'redfish', False otherwise.
|
||||
|
||||
Returns: True if bm_type of this host is 'redfish'.
|
||||
Args:
|
||||
hostname (str): Host name to get its bmc type.
|
||||
|
||||
Returns:
|
||||
bool: True if bm_type of this host is 'redfish'.
|
||||
"""
|
||||
system_host_show_object = self._get_host_value(hostname)
|
||||
return system_host_show_object.get_bm_type() == "redfish"
|
||||
|
||||
def has_host_bmc_dynamic(self, hostname: str=None) -> bool:
|
||||
def has_host_bmc_dynamic(self, hostname: str = None) -> bool:
|
||||
"""
|
||||
This function will return True if bm_type of this host is 'dynamic', False otherwise.
|
||||
|
||||
Returns: True if bm_type of this host is 'dynamic'.
|
||||
Args:
|
||||
hostname (str): Host name to get its bmc type.
|
||||
|
||||
Returns:
|
||||
bool: True if bm_type of this host is 'dynamic'.
|
||||
"""
|
||||
system_host_show_object = self._get_host_value(hostname)
|
||||
return system_host_show_object.get_bm_type() == "dynamic"
|
||||
|
||||
def get_host_id(self, hostname: str=None)-> int:
|
||||
|
||||
def get_host_id(self, hostname: str = None) -> int:
|
||||
"""
|
||||
Gets the host id
|
||||
|
||||
|
||||
Args:
|
||||
hostname (): the name of the host
|
||||
|
||||
Returns: the host id
|
||||
hostname (str): the name of the host
|
||||
|
||||
Returns:
|
||||
int: the host id
|
||||
"""
|
||||
system_host_show_object = self._get_host_value(hostname)
|
||||
return system_host_show_object.get_id()
|
||||
|
||||
def get_system_host_show_object(self, hostname: str=None) -> SystemHostShowObject:
|
||||
def get_system_host_show_object(self, hostname: str = None) -> SystemHostShowObject:
|
||||
"""
|
||||
Gets the system host show object
|
||||
|
||||
Args:
|
||||
hostname (): the name of the host
|
||||
hostname (str): the name of the host
|
||||
|
||||
Returns: the system host show object
|
||||
Returns:
|
||||
SystemHostShowObject: the system host show object
|
||||
|
||||
"""
|
||||
return self._get_host_value(hostname)
|
||||
|
||||
|
||||
def get_all_system_host_show_objects(self) -> list[SystemHostShowObject]:
|
||||
"""
|
||||
Gets all system host show objects
|
||||
|
||||
Returns: list of system host show objects
|
||||
|
||||
Returns:
|
||||
list[SystemHostShowObject]: List of system host show objects
|
||||
"""
|
||||
return self.system_host_show_objects
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
class SoftwareDeployPrecheckItemObject:
|
||||
"""
|
||||
Represents a single line/check from the 'software deploy precheck' output.
|
||||
|
||||
Example line:
|
||||
'Ceph Storage Healthy: [OK]'
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, status: str):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
Args:
|
||||
name (str): Check name (e.g., 'Ceph Storage Healthy').
|
||||
status (str): Status string (e.g., '[OK]', '[FAIL] ...').
|
||||
"""
|
||||
self._name = name
|
||||
self._status = status
|
||||
|
||||
def get_name(self) -> str:
|
||||
"""
|
||||
Get the check name.
|
||||
|
||||
Returns:
|
||||
str: Check name.
|
||||
"""
|
||||
return self._name
|
||||
|
||||
def get_status(self) -> str:
|
||||
"""
|
||||
Get the raw status.
|
||||
|
||||
Returns:
|
||||
str: Status string as returned by the command.
|
||||
"""
|
||||
return self._status
|
||||
|
||||
def is_ok(self) -> bool:
|
||||
"""
|
||||
Check if this item is considered OK based on its status string.
|
||||
|
||||
Returns:
|
||||
bool: True if the status contains "[OK]", False otherwise.
|
||||
"""
|
||||
return "[OK]" in self._status
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""
|
||||
Return a readable string representation.
|
||||
|
||||
Returns:
|
||||
str: Formatted string with name and status.
|
||||
"""
|
||||
return f"{self._name}: {self._status}"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
Return the developer-facing representation.
|
||||
|
||||
Returns:
|
||||
str: Class name and field values.
|
||||
"""
|
||||
return f"{self.__class__.__name__}(name={self._name}, status={self._status})"
|
||||
@@ -0,0 +1,115 @@
|
||||
from typing import Dict, List
|
||||
|
||||
from framework.logging.automation_logger import get_logger
|
||||
from keywords.cloud_platform.upgrade.objects.software_deploy_precheck_object import SoftwareDeployPrecheckItemObject
|
||||
|
||||
|
||||
class SoftwareDeployPrecheckOutput:
|
||||
"""
|
||||
Parses the output of the 'software deploy precheck' command into structured objects.
|
||||
|
||||
The raw output is expected to contain lines in the format:
|
||||
"<check name>: <status>"
|
||||
|
||||
Example:
|
||||
"Ceph Storage Healthy: [OK]"
|
||||
"No alarms: [OK]"
|
||||
"System Health: [OK]"
|
||||
"""
|
||||
|
||||
def __init__(self, raw_output: List[str]):
|
||||
"""
|
||||
Initialize and parse the precheck output.
|
||||
|
||||
Args:
|
||||
raw_output (List[str]): Raw output lines from 'software deploy precheck'.
|
||||
"""
|
||||
self._raw_output = raw_output
|
||||
self._items: List[SoftwareDeployPrecheckItemObject] = []
|
||||
self._status_by_name: Dict[str, str] = {}
|
||||
|
||||
self._parse_output()
|
||||
|
||||
def _parse_output(self) -> None:
|
||||
"""
|
||||
Internal parsing of the raw output into objects and a name->status mapping.
|
||||
|
||||
"""
|
||||
lines_to_parse = self._raw_output[:-1]
|
||||
for line in lines_to_parse:
|
||||
if ":" not in line:
|
||||
get_logger().log_warning(f"There is unexpected output {line}")
|
||||
continue
|
||||
|
||||
key, value = line.split(":", 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
|
||||
self._status_by_name[key] = value
|
||||
self._items.append(SoftwareDeployPrecheckItemObject(name=key, status=value))
|
||||
|
||||
def get_items(self) -> List[SoftwareDeployPrecheckItemObject]:
|
||||
"""
|
||||
Get all parsed precheck items.
|
||||
|
||||
Returns:
|
||||
List[SoftwareDeployPrecheckItemObject]: Parsed items.
|
||||
"""
|
||||
return self._items
|
||||
|
||||
def get_status_dict(self) -> Dict[str, str]:
|
||||
"""
|
||||
Get a mapping of check name -> status string.
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: Status by check name.
|
||||
"""
|
||||
return self._status_by_name
|
||||
|
||||
def get_status_by_name(self, name: str) -> str:
|
||||
"""
|
||||
Get the status string for a specific check.
|
||||
|
||||
Args:
|
||||
name (str): Check name.
|
||||
|
||||
Returns:
|
||||
str: Status string or empty string if not found.
|
||||
"""
|
||||
return self._status_by_name.get(name, "")
|
||||
|
||||
def get_failed_items(self) -> List[SoftwareDeployPrecheckItemObject]:
|
||||
"""
|
||||
Get all items that are not marked as OK.
|
||||
|
||||
Returns:
|
||||
List[SoftwareDeployPrecheckItemObject]: Items where status does not contain "[OK]".
|
||||
"""
|
||||
return [item for item in self._items if not item.is_ok()]
|
||||
|
||||
def get_raw_output(self) -> List[str]:
|
||||
"""
|
||||
Get the raw output lines.
|
||||
|
||||
Returns:
|
||||
List[str]: Raw command output.
|
||||
"""
|
||||
return self._raw_output
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""
|
||||
Return a human-readable string representation of the precheck.
|
||||
|
||||
Returns:
|
||||
str: Formatted precheck entries as strings.
|
||||
"""
|
||||
return "\n".join([str(item) for item in self._items])
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
Return the developer-facing representation of the object.
|
||||
|
||||
Returns:
|
||||
str: Class name and row count.
|
||||
"""
|
||||
return f"{self.__class__.__name__}(items={len(self._items)})"
|
||||
@@ -0,0 +1,173 @@
|
||||
"""Software deploy precheck keywords."""
|
||||
|
||||
from config.configuration_manager import ConfigurationManager
|
||||
from framework.exceptions.keyword_exception import KeywordException
|
||||
from framework.logging.automation_logger import get_logger
|
||||
from framework.ssh.ssh_connection import SSHConnection
|
||||
from keywords.base_keyword import BaseKeyword
|
||||
from keywords.ceph.ceph_status_keywords import CephStatusKeywords
|
||||
from keywords.cloud_platform.command_wrappers import source_openrc
|
||||
from keywords.cloud_platform.fault_management.alarms.alarm_list_keywords import AlarmListKeywords
|
||||
from keywords.cloud_platform.system.host.system_host_list_keywords import SystemHostListKeywords
|
||||
from keywords.cloud_platform.system.host.system_host_show_keywords import SystemHostShowKeywords
|
||||
from keywords.cloud_platform.upgrade.objects.software_deploy_precheck_output import SoftwareDeployPrecheckOutput
|
||||
from keywords.k8s.node.kubectl_nodes_keywords import KubectlNodesKeywords
|
||||
from keywords.k8s.pods.kubectl_get_pods_keywords import KubectlGetPodsKeywords
|
||||
|
||||
|
||||
class SoftwareDeployPrecheckKeywords(BaseKeyword):
|
||||
"""
|
||||
Keywords for 'software deploy precheck' using the ACE object-output model.
|
||||
|
||||
This class:
|
||||
- runs the 'software deploy precheck' command
|
||||
- wraps the CLI output into SoftwareDeployPrecheckOutput
|
||||
- performs additional cross-checks against system state
|
||||
"""
|
||||
|
||||
def __init__(self, ssh_connection: SSHConnection):
|
||||
"""
|
||||
Instance of the class.
|
||||
|
||||
Args:
|
||||
ssh_connection (SSHConnection): An instance of SSH connection.
|
||||
"""
|
||||
self.ssh_connection = ssh_connection
|
||||
self.usm_config = ConfigurationManager.get_usm_config()
|
||||
|
||||
def _run_deploy_precheck(self, release_id: str, sudo: bool = False) -> SoftwareDeployPrecheckOutput:
|
||||
"""
|
||||
Run the 'software deploy precheck' command and return its parsed output.
|
||||
|
||||
Args:
|
||||
release_id (str): Release to be prechecked.
|
||||
sudo (bool): Option to pass the command with sudo.
|
||||
|
||||
Returns:
|
||||
SoftwareDeployPrecheckOutput: Parsed precheck output.
|
||||
|
||||
Raises:
|
||||
KeywordException: If the CLI command fails.
|
||||
"""
|
||||
if not release_id:
|
||||
raise KeywordException("Missing release ID for software deploy precheck")
|
||||
|
||||
get_logger().log_info(f"Prechecking deploy software release: {release_id}")
|
||||
base_cmd = f"software deploy precheck {release_id}"
|
||||
cmd = source_openrc(base_cmd)
|
||||
timeout = self.usm_config.get_precheck_timeout_sec()
|
||||
|
||||
if sudo:
|
||||
output = self.ssh_connection.send_as_sudo(cmd, reconnect_timeout=timeout)
|
||||
else:
|
||||
output = self.ssh_connection.send(cmd, reconnect_timeout=timeout, get_pty=True)
|
||||
|
||||
# Validate the return code using the base keyword helper.
|
||||
self.validate_success_return_code(self.ssh_connection)
|
||||
|
||||
# Wrap the output into the object-output model.
|
||||
precheck_output = SoftwareDeployPrecheckOutput(output)
|
||||
return precheck_output
|
||||
|
||||
def _validate_precheck_output(self, precheck_output: SoftwareDeployPrecheckOutput) -> bool:
|
||||
"""
|
||||
Validate the precheck output by cross-checking with the actual system state.
|
||||
|
||||
Args:
|
||||
precheck_output (SoftwareDeployPrecheckOutput): Parsed precheck output.
|
||||
|
||||
Returns:
|
||||
bool: True if validation passes, False otherwise.
|
||||
"""
|
||||
ceph_status = CephStatusKeywords(self.ssh_connection).ceph_status()
|
||||
alarm_list = AlarmListKeywords(self.ssh_connection).alarm_list()
|
||||
system_hosts = SystemHostListKeywords(self.ssh_connection)
|
||||
system_host_show = SystemHostShowKeywords(self.ssh_connection)
|
||||
hosts = system_hosts.get_system_host_list().get_hosts()
|
||||
|
||||
status_dict = precheck_output.get_status_dict()
|
||||
|
||||
for key, value in status_dict.items():
|
||||
if "[OK]" in value:
|
||||
get_logger().log_info(f"'{key}' is OK")
|
||||
|
||||
if key == "Ceph Storage Healthy" and not ceph_status.is_ceph_healthy():
|
||||
get_logger().log_warning(f"Ceph is not healthy but '{key}' value is OK")
|
||||
return False
|
||||
|
||||
if key == "No alarms" and [] != alarm_list:
|
||||
get_logger().log_warning(f"There are one or more alarms but '{key}' value is OK")
|
||||
return False
|
||||
|
||||
if key == "All hosts are provisioned":
|
||||
for host in hosts:
|
||||
system_host_show_object = system_host_show.get_system_host_show_output(host.get_host_name()).get_system_host_show_object()
|
||||
provisioned = True if system_host_show_object.get_invprovision() == "provisioned" else False
|
||||
# Don't remove this validation, to avoid bool failures.
|
||||
if not provisioned:
|
||||
get_logger().log_warning(f"The host {host} is not provisioned but {key} value is Ok")
|
||||
return False
|
||||
|
||||
if key == "All hosts are unlocked/enabled":
|
||||
for host in hosts:
|
||||
if host.get_administrative == "locked":
|
||||
get_logger().log_warning(f"There are one or more locked hosts but '{key}' value is OK")
|
||||
return False
|
||||
if host.get_operational() == "disabled":
|
||||
get_logger().log_warning(f"There are one or more disabled hosts but '{key}' value is OK")
|
||||
return False
|
||||
|
||||
if key == "All hosts have current configurations":
|
||||
for host in hosts:
|
||||
system_host_show_object = system_host_show.get_system_host_show_output(host.get_host_name()).get_system_host_show_object()
|
||||
config_applied = system_host_show_object.get_config_applied()
|
||||
config_target = system_host_show_object.get_config_target()
|
||||
if config_applied != config_target:
|
||||
get_logger().log_warning("There are one or host with failed " f"configuration but '{key}' value is OK")
|
||||
return False
|
||||
|
||||
if key == "All kubernetes nodes are ready":
|
||||
nodes = KubectlNodesKeywords(self.ssh_connection).get_kubectl_nodes().get_nodes()
|
||||
for node in nodes:
|
||||
if node.get_status() != "Ready":
|
||||
get_logger().log_warning("There are one or more kubernetes nodes not ready " f"but '{key}' value is OK")
|
||||
return False
|
||||
|
||||
if key == "All kubernetes control plane pods are ready":
|
||||
kube_get_pods = KubectlGetPodsKeywords(self.ssh_connection)
|
||||
if kube_get_pods.get_unhealthy_pods().get_pods():
|
||||
get_logger().log_warning(f"There are one or more failed pods but '{key}' value is OK")
|
||||
return False
|
||||
|
||||
if key == "Active controller is controller-0" and system_hosts.get_active_controller().get_host_name() != "controller-0":
|
||||
get_logger().log_warning(f"controller-0 is not active but '{key}' value is OK")
|
||||
return False
|
||||
|
||||
else:
|
||||
if key != "System Health":
|
||||
get_logger().log_warning(f"The following check '{key}' is not OK")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def deploy_precheck(self, release_id: str, sudo: bool = False) -> SoftwareDeployPrecheckOutput:
|
||||
"""
|
||||
Run the deploy precheck for a software release and validate its result.
|
||||
|
||||
Args:
|
||||
release_id (str): Release to be prechecked.
|
||||
sudo (bool): Option to pass the command with sudo.
|
||||
|
||||
Returns:
|
||||
SoftwareDeployPrecheckOutput: Parsed and validated precheck output.
|
||||
|
||||
Raises:
|
||||
AssertionError: If any of the checks fail.
|
||||
"""
|
||||
precheck_output = self._run_deploy_precheck(release_id, sudo=sudo)
|
||||
is_valid = self._validate_precheck_output(precheck_output)
|
||||
assert is_valid, f"There is failed resource in the deploy precheck. Output: {precheck_output.get_raw_output()}"
|
||||
|
||||
get_logger().log_info("Deploy precheck completed:\n" + "\n".join(precheck_output.get_raw_output()))
|
||||
|
||||
return precheck_output
|
||||
@@ -1,12 +1,12 @@
|
||||
from config.configuration_manager import ConfigurationManager
|
||||
from framework.exceptions.keyword_exception import KeywordException
|
||||
from framework.logging.automation_logger import get_logger
|
||||
from framework.ssh.ssh_connection import SSHConnection
|
||||
from framework.validation.validation import validate_equals_with_retry
|
||||
from keywords.base_keyword import BaseKeyword
|
||||
from keywords.cloud_platform.upgrade.objects.software_upload_output import SoftwareUploadOutput
|
||||
from keywords.cloud_platform.command_wrappers import source_openrc
|
||||
from keywords.cloud_platform.upgrade.objects.software_upload_output import SoftwareUploadOutput
|
||||
from keywords.cloud_platform.upgrade.software_show_keywords import SoftwareShowKeywords
|
||||
from config.configuration_manager import ConfigurationManager
|
||||
|
||||
|
||||
class USMKeywords(BaseKeyword):
|
||||
@@ -20,14 +20,14 @@ class USMKeywords(BaseKeyword):
|
||||
self.ssh_connection = ssh_connection
|
||||
self.usm_config = ConfigurationManager.get_usm_config()
|
||||
|
||||
def upload_patch_file(self, patch_file_path: str, sudo: bool = False, os_region_name: str = "" ) -> SoftwareUploadOutput:
|
||||
def upload_patch_file(self, patch_file_path: str, sudo: bool = False, os_region_name: str = "") -> SoftwareUploadOutput:
|
||||
"""
|
||||
Upload a single patch file using 'software upload'.
|
||||
|
||||
Args:
|
||||
patch_file_path (str): Absolute path to a .patch file.
|
||||
sudo (bool): Option to pass the command with sudo.
|
||||
os_region_name: Use Os region name option for upload if it is specified
|
||||
os_region_name (str): Use Os region name option for upload if it is specified
|
||||
|
||||
Raises:
|
||||
KeywordException: On failure to upload.
|
||||
@@ -57,7 +57,7 @@ class USMKeywords(BaseKeyword):
|
||||
Args:
|
||||
patch_dir_path (str): Absolute path to a directory of .patch files.
|
||||
sudo (bool): Option to pass the command with sudo.
|
||||
os_region_name: OS region name option for upload if it is specified
|
||||
os_region_name (str): OS region name option for upload if it is specified
|
||||
|
||||
Raises:
|
||||
KeywordException: On failure to upload.
|
||||
@@ -105,7 +105,7 @@ class USMKeywords(BaseKeyword):
|
||||
iso_path (str): Absolute path to the .iso file.
|
||||
sig_path (str): Absolute path to the corresponding .sig file.
|
||||
sudo (bool): Option to pass the command with sudo.
|
||||
os_region_name: Use Os region name option for upload if it is specified
|
||||
os_region_name (str): Use Os region name option for upload if it is specified
|
||||
|
||||
Raises:
|
||||
KeywordException: On failure to upload.
|
||||
@@ -124,7 +124,7 @@ class USMKeywords(BaseKeyword):
|
||||
self.validate_success_return_code(self.ssh_connection)
|
||||
get_logger().log_info("Release upload completed:\n" + "\n".join(output))
|
||||
|
||||
def upload_and_verify_patch_file(self, patch_file_path: str, expected_release_id: str, timeout: int, poll_interval: int, sudo: bool = False, os_region_name: str="") -> None:
|
||||
def upload_and_verify_patch_file(self, patch_file_path: str, expected_release_id: str, timeout: int, poll_interval: int, sudo: bool = False, os_region_name: str = "") -> None:
|
||||
"""Upload a patch and verify that it becomes available.
|
||||
|
||||
This method is used for USM patching operations. It uploads a `.patch` file
|
||||
@@ -137,7 +137,8 @@ class USMKeywords(BaseKeyword):
|
||||
timeout (int): Maximum number of seconds to wait for the release to appear.
|
||||
poll_interval (int): Interval (in seconds) between poll attempts.
|
||||
sudo (bool): Option to pass the command with sudo.
|
||||
os_region_name: Use Os region name option for upload if it is specified
|
||||
os_region_name (str): Use Os region name option for upload if it is specified
|
||||
|
||||
|
||||
Raises:
|
||||
KeywordException: If upload fails or release does not become available in time.
|
||||
@@ -166,7 +167,7 @@ class USMKeywords(BaseKeyword):
|
||||
timeout (int): Maximum number of seconds to wait for the release to appear.
|
||||
poll_interval (int): Interval (in seconds) between poll attempts.
|
||||
sudo (bool): Option to pass the command with sudo.
|
||||
os_region_name (str): Region name used when upload to the DC Systems
|
||||
os_region_name (str): Use Os region name option for upload if it is specified
|
||||
|
||||
Raises:
|
||||
KeywordException: If upload fails or release does not become available in time.
|
||||
|
||||
34
keywords/k8s/node/kubectl_nodes_keywords.py
Normal file
34
keywords/k8s/node/kubectl_nodes_keywords.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from framework.ssh.ssh_connection import SSHConnection
|
||||
from keywords.base_keyword import BaseKeyword
|
||||
from keywords.cloud_platform.command_wrappers import source_openrc
|
||||
from keywords.k8s.k8s_command_wrapper import export_k8s_config
|
||||
from keywords.k8s.node.object.kubectl_nodes_output import KubectlNodesOutput
|
||||
|
||||
|
||||
class KubectlNodesKeywords(BaseKeyword):
|
||||
"""
|
||||
Class for Kubectl "kubectl get nodes" keywords
|
||||
"""
|
||||
|
||||
def __init__(self, ssh_connection: SSHConnection):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
Args:
|
||||
ssh_connection(SSHConnection): SSH Connection object
|
||||
"""
|
||||
self.ssh_connection = ssh_connection
|
||||
|
||||
def get_kubectl_nodes(self) -> KubectlNodesOutput:
|
||||
"""
|
||||
Gets the kubectl get nodes
|
||||
|
||||
Returns:
|
||||
KubectlNodesOutput: KubectlNodesOutput object
|
||||
|
||||
"""
|
||||
output = self.ssh_connection.send(source_openrc(export_k8s_config("kubectl get nodes")))
|
||||
self.validate_success_return_code(self.ssh_connection)
|
||||
kubectl_nodes_output = KubectlNodesOutput(output)
|
||||
|
||||
return kubectl_nodes_output
|
||||
102
keywords/k8s/node/object/kubectl_nodes_object.py
Normal file
102
keywords/k8s/node/object/kubectl_nodes_object.py
Normal file
@@ -0,0 +1,102 @@
|
||||
class KubectlNodesObject:
|
||||
"""
|
||||
Class to hold attributes of a 'kubectl get nodes' entry.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
"""
|
||||
self.name: str = None
|
||||
self.status: str = None
|
||||
self.roles: str = None
|
||||
self.age: str = None
|
||||
self.version: str = None
|
||||
|
||||
def set_name(self, name: str):
|
||||
"""
|
||||
Setter for the name
|
||||
|
||||
Args:
|
||||
name(str): Name of the Node
|
||||
"""
|
||||
self.name = name
|
||||
|
||||
def get_name(self) -> str:
|
||||
"""
|
||||
Getter for the name of the node.
|
||||
|
||||
Returns: (str) name of the node.
|
||||
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def set_status(self, status: str):
|
||||
"""
|
||||
Setter for the status
|
||||
|
||||
Args:
|
||||
status (str): Status of the Node
|
||||
"""
|
||||
self.status = status
|
||||
|
||||
def get_status(self) -> str:
|
||||
"""
|
||||
Getter for the status of the node.
|
||||
|
||||
Returns: (str) status of the node.
|
||||
"""
|
||||
return self.status
|
||||
|
||||
def set_roles(self, roles: str):
|
||||
"""
|
||||
Setter for the roles
|
||||
|
||||
Args:
|
||||
roles(str): Roles of the Node
|
||||
"""
|
||||
self.roles = roles
|
||||
|
||||
def get_roles(self) -> str:
|
||||
"""
|
||||
Getter for the roles of the node.
|
||||
|
||||
Returns: (str) roles of the node.
|
||||
"""
|
||||
return self.roles
|
||||
|
||||
def set_age(self, age: str):
|
||||
"""
|
||||
Setter for the age of the node
|
||||
|
||||
Args:
|
||||
age(str): Age of the Node
|
||||
"""
|
||||
self.age = age
|
||||
|
||||
def get_age(self) -> str:
|
||||
"""
|
||||
Getter for the age of the node.
|
||||
|
||||
Returns: (str) Age of the node.
|
||||
"""
|
||||
return self.age
|
||||
|
||||
def set_version(self, version: str):
|
||||
"""
|
||||
Setter for the version of the node
|
||||
|
||||
Args:
|
||||
version(str): Version of the Node
|
||||
"""
|
||||
self.version = version
|
||||
|
||||
def get_version(self) -> str:
|
||||
"""
|
||||
Getter for the version of the node.
|
||||
|
||||
Returns:
|
||||
str: Version of the node.
|
||||
"""
|
||||
return self.version
|
||||
87
keywords/k8s/node/object/kubectl_nodes_output.py
Normal file
87
keywords/k8s/node/object/kubectl_nodes_output.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from framework.exceptions.keyword_exception import KeywordException
|
||||
from framework.logging.automation_logger import get_logger
|
||||
from keywords.k8s.node.object.kubectl_nodes_object import KubectlNodesObject
|
||||
from keywords.k8s.node.object.kubectl_nodes_table_parser import KubectlNodesTableParser
|
||||
|
||||
|
||||
class KubectlNodesOutput:
|
||||
"""
|
||||
Class for 'kubectl get nodes' output.
|
||||
"""
|
||||
|
||||
def __init__(self, kubectl_nodes_output: str):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
Args:
|
||||
kubectl_nodes_output(str): Raw string output from running a "kubectl get nodes" command.
|
||||
|
||||
"""
|
||||
self.kubectl_nodes: list[KubectlNodesObject] = []
|
||||
k8s_table_parser = KubectlNodesTableParser(kubectl_nodes_output)
|
||||
output_values = k8s_table_parser.get_output_values_list()
|
||||
for value in output_values:
|
||||
if self.is_valid_output(value):
|
||||
kubectl_nodes_object = KubectlNodesObject()
|
||||
kubectl_nodes_object.set_name(value["NAME"])
|
||||
kubectl_nodes_object.set_status(value["STATUS"])
|
||||
kubectl_nodes_object.set_age(value["AGE"])
|
||||
kubectl_nodes_object.set_version(value["VERSION"])
|
||||
self.kubectl_nodes.append(kubectl_nodes_object)
|
||||
else:
|
||||
raise KeywordException(f"The output line {value} was not valid")
|
||||
|
||||
def get_nodes(self) -> list[KubectlNodesObject]:
|
||||
"""
|
||||
Returns the list of kubectl nodes objects.
|
||||
|
||||
Returns:
|
||||
list[KubectlNodesObject]: List of KubectlNodesObject
|
||||
"""
|
||||
return self.kubectl_nodes
|
||||
|
||||
def get_node(self, node_name: str) -> KubectlNodesObject:
|
||||
"""
|
||||
Returns the node with the given name
|
||||
|
||||
Args:
|
||||
node_name (str): the name of the node
|
||||
|
||||
Returns:
|
||||
KubectlNodesObject: kubectl node object
|
||||
"""
|
||||
nodes = list(filter(lambda node: node.get_name() == node_name, self.kubectl_nodes))
|
||||
if len(nodes) == 0:
|
||||
raise KeywordException(f"No Node with name {node_name} was found.")
|
||||
|
||||
return nodes[0]
|
||||
|
||||
@staticmethod
|
||||
def is_valid_output(value: dict) -> bool:
|
||||
"""
|
||||
Checks to ensure the output has the correct keys.
|
||||
|
||||
Args:
|
||||
value (dict): The value to check.
|
||||
|
||||
Returns:
|
||||
bool: True if valid, False otherwise.
|
||||
"""
|
||||
valid = True
|
||||
if "NAME" not in value:
|
||||
get_logger().log_error(f"NAME is not in the output value: {value}")
|
||||
valid = False
|
||||
if "STATUS" not in value:
|
||||
get_logger().log_error(f"STATUS is not in the output value: {value}")
|
||||
valid = False
|
||||
if "ROLES" not in value:
|
||||
get_logger().log_error(f"ROLES is not in the output value: {value}")
|
||||
valid = False
|
||||
if "AGE" not in value:
|
||||
get_logger().log_error(f"AGE is not in the output value: {value}")
|
||||
valid = False
|
||||
if "VERSION" not in value:
|
||||
get_logger().log_error(f"VERSION is not in the output value: {value}")
|
||||
valid = False
|
||||
|
||||
return valid
|
||||
17
keywords/k8s/node/object/kubectl_nodes_table_parser.py
Normal file
17
keywords/k8s/node/object/kubectl_nodes_table_parser.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from keywords.k8s.k8s_table_parser_base import K8sTableParserBase
|
||||
|
||||
|
||||
class KubectlNodesTableParser(K8sTableParserBase):
|
||||
"""
|
||||
Class for parsing the output of "kubectl get nodes" command.
|
||||
"""
|
||||
|
||||
def __init__(self, k8s_output: str):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
Args:
|
||||
k8s_output(str): The raw String output of a kubernetes command that returns a table.
|
||||
"""
|
||||
super().__init__(k8s_output)
|
||||
self.possible_headers = ["NAME", "STATUS", "ROLES", "AGE", "VERSION"]
|
||||
@@ -86,6 +86,20 @@ class KubectlGetPodsKeywords(BaseKeyword):
|
||||
|
||||
return pods_list_output
|
||||
|
||||
def get_unhealthy_pods(self) -> KubectlGetPodsOutput:
|
||||
"""
|
||||
Get the k8s pods that are unhealthy
|
||||
|
||||
Returns:
|
||||
KubectlGetPodsOutput: An object containing the parsed output of the command.
|
||||
"""
|
||||
field_selector = "status.phase!=Running,status.phase!=Succeeded"
|
||||
kubectl_get_pods_output = self.ssh_connection.send(export_k8s_config(f"kubectl get pods --all-namespaces --field-selector={field_selector}"))
|
||||
self.validate_success_return_code(self.ssh_connection)
|
||||
pods_list_output = KubectlGetPodsOutput(kubectl_get_pods_output)
|
||||
|
||||
return pods_list_output
|
||||
|
||||
def wait_for_pod_max_age(self, pod_name: str, max_age: int, namespace: str = None, timeout: int = 600, check_interval: int = 20) -> bool:
|
||||
"""
|
||||
Wait for the pod to be in a certain max_age.
|
||||
|
||||
Reference in New Issue
Block a user