diff --git a/config/lab/objects/lab_config.py b/config/lab/objects/lab_config.py index e395c29d..57d36afb 100644 --- a/config/lab/objects/lab_config.py +++ b/config/lab/objects/lab_config.py @@ -459,3 +459,14 @@ class LabConfig: log_strings.append(f" {log_string}") return log_strings + + def get_controllers(self) -> List[Node]: + """ + Getter for controller nodes. + + Returns: + List[Node]: List of controller nodes + """ + nodes = self.get_nodes() + controllers = [node for node in nodes if node.node_type == "controller"] + return controllers diff --git a/keywords/bmc/ipmitool/chassis/power/ipmitool_chassis_power_keywords.py b/keywords/bmc/ipmitool/chassis/power/ipmitool_chassis_power_keywords.py index 5755b769..fc0ff515 100644 --- a/keywords/bmc/ipmitool/chassis/power/ipmitool_chassis_power_keywords.py +++ b/keywords/bmc/ipmitool/chassis/power/ipmitool_chassis_power_keywords.py @@ -13,23 +13,39 @@ class IPMIToolChassisPowerKeywords(BaseKeyword): lab_config = ConfigurationManager.get_lab_config() self.bm_password = lab_config.get_bm_password() + if host_name: + node = lab_config.get_node(host_name) + self.bm_ip = node.get_bm_ip() + self.bm_username = node.get_bm_username() - node = lab_config.get_node(host_name) - self.bm_ip = node.get_bm_ip() - self.bm_username = node.get_bm_username() + def _power_off(self, bm_ip: str, bm_username: str, bm_password: str): + """Powers off the host using IPMI tool + + Args: + bm_ip (str): IP address of the BMC + bm_username (str): Username for BMC + bm_password (str): Password for BMC + """ + self.ssh_connection.send(f"ipmitool -I lanplus -H {bm_ip} -U {bm_username} -P {bm_password} chassis power off") def power_on(self): - """ - Powers on the host - Returns: - - """ + """Powers on the host""" self.ssh_connection.send(f"ipmitool -I lanplus -H {self.bm_ip} -U {self.bm_username} -P {self.bm_password} chassis power on") def power_off(self): - """ - Powers off the host - Returns: + """Powers off the host""" + self._power_off(self.bm_ip, self.bm_username, self.bm_password) + def power_off_subcloud(self, subcloud_name: str): + """Powers off the host + + Args: + subcloud_name (str): name of the subcloud to be powered off """ - self.ssh_connection.send(f"ipmitool -I lanplus -H {self.bm_ip} -U {self.bm_username} -P {self.bm_password} chassis power off") + sc_config = ConfigurationManager.get_lab_config().get_subcloud(subcloud_name) + controllers = sc_config.get_controllers() + bm_password = sc_config.get_bm_password() + + for controller in controllers: + self._power_off(controller.get_bm_ip(), controller.get_bm_username(), bm_password) + self.bm_ip = controller.get_bm_ip() diff --git a/keywords/cloud_platform/dcmanager/dcmanager_subcloud_manager_keywords.py b/keywords/cloud_platform/dcmanager/dcmanager_subcloud_manager_keywords.py index 46b801eb..a00e7528 100644 --- a/keywords/cloud_platform/dcmanager/dcmanager_subcloud_manager_keywords.py +++ b/keywords/cloud_platform/dcmanager/dcmanager_subcloud_manager_keywords.py @@ -3,29 +3,27 @@ import time from framework.logging.automation_logger import get_logger from framework.ssh.ssh_connection import SSHConnection from keywords.base_keyword import BaseKeyword +from keywords.bmc.ipmitool.chassis.power.ipmitool_chassis_power_keywords import IPMIToolChassisPowerKeywords from keywords.cloud_platform.command_wrappers import source_openrc from keywords.cloud_platform.dcmanager.dcmanager_subcloud_list_keywords import DcManagerSubcloudListKeywords from keywords.cloud_platform.dcmanager.objects.dcmanager_subcloud_list_object_filter import DcManagerSubcloudListObjectFilter from keywords.cloud_platform.dcmanager.objects.dcmanager_subcloud_manage_output import DcManagerSubcloudManageOutput -from keywords.cloud_platform.ssh.lab_connection_keywords import LabConnectionKeywords class DcManagerSubcloudManagerKeywords(BaseKeyword): - """ - This class contains all the keywords related to the 'dcmanager subcloud ' and - command. - """ + """This class contains all the keywords related to the 'dcmanager subcloud ' and command.""" def __init__(self, ssh_connection: SSHConnection): - """ - Constructor + """Constructor + Args: ssh_connection (SSHConnection): The SSH connection to the central subcloud. """ self.ssh_connection = ssh_connection def get_dcmanager_subcloud_manage(self, subcloud_name: str, timeout: int) -> DcManagerSubcloudManageOutput: - """ + """Dcmanager subcloud manage + Gets the output of 'dcmanager subcloud manage ' as an instance of DcManagerSubcloudManageOutput if the subcloud is successfully set to 'managed' (its 'management' attribute updated to 'managed') within the specified 'timeout' period. Otherwise, this method returns None. @@ -42,7 +40,8 @@ class DcManagerSubcloudManagerKeywords(BaseKeyword): return self._get_dcmanager_subcloud_operation(subcloud_name, timeout, "manage") def get_dcmanager_subcloud_unmanage(self, subcloud_name: str, timeout: int) -> DcManagerSubcloudManageOutput: - """ + """Dcmanager subcloud unmanage + Gets the output of 'dcmanager subcloud unmanage ' as an instance of DcManagerSubcloudManageOutput if the subcloud is successfully set to 'unmanaged' (its 'management' attribute updated to 'unmanaged') within the specified 'timeout' period. Otherwise, this method returns None. @@ -59,7 +58,8 @@ class DcManagerSubcloudManagerKeywords(BaseKeyword): return self._get_dcmanager_subcloud_operation(subcloud_name, timeout, "unmanage") def _get_dcmanager_subcloud_operation(self, subcloud_name: str, end_time: int, operation: str) -> DcManagerSubcloudManageOutput: - """ + """Dcmanager subcloud manage/unmanage + Gets the output of 'dcmanager subcloud ' as an instance of DcManagerSubcloudManageOutput if the subcloud is successfully set to 'managed' or 'unmanaged' (its 'management' attribute updated to 'managed' or to 'unmanaged', depending on 'operation') within the specified 'timeout' period. Otherwise, this method @@ -68,11 +68,15 @@ class DcManagerSubcloudManagerKeywords(BaseKeyword): Args: subcloud_name (str): The name of the subcloud that must be set to the 'managed' state. end_time (int): The maximum time, in seconds, to wait for the subcloud to enter the 'managed' state. + operation (str): The operation to be performed on the subcloud ('manage' or 'unmanage'). Returns: DcManagerSubcloudManageOutput: An instance representing the output of the command 'dcmanager subcloud ' if successful, or None otherwise. + Raises: + TimeoutError: If the subcloud does not enter the 'managed' or 'unmanaged' state within the specified timeout. + """ target_state = "managed" if operation == "unmanage": @@ -112,24 +116,17 @@ class DcManagerSubcloudManagerKeywords(BaseKeyword): Raises: TimeoutError """ # get SSH connection to subcloud - subcloud_ssh = LabConnectionKeywords().get_subcloud_ssh(subcloud_name) - # power off the subcloud - # TODO find a better way to do it. as DC libvirt has no IPMI , To make work for both - # labs and virtual labs we need to find a way to power off the subcloud - subcloud_ssh.send_as_sudo("shutdown -h now") - self.validate_success_return_code(subcloud_ssh) - + # power off all the controllers in the subcloud + IPMIToolChassisPowerKeywords(self.ssh_connection, None).power_off_subcloud(subcloud_name) # This section is responsible for verifying whether the state has changed within a defined timeout. target_state = "offline" end_time = time.time() + timeout while time.time() < end_time: dcm_sc_list_kw = DcManagerSubcloudListKeywords(self.ssh_connection) - subclouds_list = dcm_sc_list_kw.get_dcmanager_subcloud_list() - subclouds = subclouds_list.get_dcmanager_subcloud_list_objects() - for sc in subclouds: - if sc.get_name() == subcloud_name and sc.get_availability() == target_state: - return True - # If the subcloud is not in the list or its availability is not 'offline', wait and check again + subcloud = dcm_sc_list_kw.get_dcmanager_subcloud_list().get_subcloud_by_name(subcloud_name) + if subcloud.get_availability() == target_state: + return True + # If the subcloud is not 'offline', wait and check again time.sleep(5) error_message = f"Failed to change the state of subcloud '{subcloud_name}' to '{target_state}'." get_logger().log_error(error_message) diff --git a/testcases/cloud_platform/sanity/test_dc_sanity.py b/testcases/cloud_platform/sanity/test_dc_sanity.py index 120944fb..27bfc02e 100644 --- a/testcases/cloud_platform/sanity/test_dc_sanity.py +++ b/testcases/cloud_platform/sanity/test_dc_sanity.py @@ -6,6 +6,8 @@ from config.configuration_manager import ConfigurationManager from framework.logging.automation_logger import get_logger from framework.validation.validation import validate_equals from keywords.cloud_platform.dcmanager.dcmanager_subcloud_add_keywords import DcManagerSubcloudAddKeywords +from keywords.cloud_platform.dcmanager.dcmanager_subcloud_delete_keywords import DcManagerSubcloudDeleteKeywords +from keywords.cloud_platform.dcmanager.dcmanager_subcloud_list_keywords import DcManagerSubcloudListKeywords from keywords.cloud_platform.dcmanager.dcmanager_subcloud_manager_keywords import DcManagerSubcloudManagerKeywords from keywords.cloud_platform.ssh.lab_connection_keywords import LabConnectionKeywords from keywords.cloud_platform.system.host.system_host_list_keywords import SystemHostListKeywords @@ -58,6 +60,41 @@ def subcloud_add(subcloud_name: str): get_logger().log_info(f"The management state of the subcloud {subcloud_name} {manage_status}") +def subcloud_delete(subcloud_name: str): + """Delete a subcloud from the system. + + Args: + subcloud_name (str): name of the subcloud to be deleted + """ + # Gets the SSH connection to the active controller of the central cloud. + ssh_connection = LabConnectionKeywords().get_active_controller_ssh() + dcm_sc_list_kw = DcManagerSubcloudListKeywords(ssh_connection) + subcloud = dcm_sc_list_kw.get_dcmanager_subcloud_list().get_subcloud_by_name(subcloud_name) + sc_name = subcloud.get_name() + msg = (f"Subcloud selected for deletion ID={subcloud.get_id()} ", f" Name={sc_name}, ", f" Management state={subcloud.get_management()} ") + get_logger().log_info(msg) + dcm_sc_manager_kw = DcManagerSubcloudManagerKeywords(ssh_connection) + # poweroff the subcloud. + get_logger().log_test_case_step(f"Poweroff subcloud={sc_name}.") + dcm_sc_manager_kw.set_subcloud_poweroff(sc_name) + # Unmanage the subcloud. + if subcloud.get_management() == "managed": + dcm_sc_manager_kw = DcManagerSubcloudManagerKeywords(ssh_connection) + get_logger().log_test_case_step(f"Unmanage subcloud={sc_name}.") + dcm_sc_manage_output = dcm_sc_manager_kw.get_dcmanager_subcloud_unmanage(sc_name, timeout=10) + get_logger().log_info(f"The management state of the subcloud {sc_name} was changed to {dcm_sc_manage_output.get_dcmanager_subcloud_manage_object().get_management()}.") + + # delete the subcloud + get_logger().log_test_case_step(f"Delete subcloud={sc_name}.") + dcm_sc_del_kw = DcManagerSubcloudDeleteKeywords(ssh_connection) + dcm_sc_del_kw.dcmanager_subcloud_delete(sc_name) + + # validate that the subcloud is deleted + subclouds_list = dcm_sc_list_kw.get_dcmanager_subcloud_list() + get_logger().log_test_case_step(f"Validate that subcloud={sc_name} is deleted.") + validate_equals(subclouds_list.is_subcloud_in_output(sc_name), False, f"{sc_name} is no longer in the subcloud list.") + + @mark.p0 @mark.lab_has_min_2_subclouds def test_dc_subcloud_add_simplex(): @@ -116,3 +153,39 @@ def test_dc_subcloud_add_duplex(): # read the config file for subcloud subcloud_name = "subcloud2" subcloud_add(subcloud_name) + + +@mark.p0 +@mark.lab_has_min_2_subclouds +def test_dc_subcloud_delete_simplex(): + """ + Verify subcloud deletion works as expected + + Test Steps: + - log onto system controller + - list all subclouds and get the lowest id subcloud + - unmanage the subcloud + - power off the subcloud + - delete the subcloud + - validate that the subcloud is deleted + """ + subcloud_name = "subcloud1" + subcloud_delete(subcloud_name) + + +@mark.p0 +@mark.lab_has_min_2_subclouds +def test_dc_subcloud_delete_duplex(): + """ + Verify subcloud deletion works as expected + + Test Steps: + - log onto system controller + - list all subclouds and get the lowest id subcloud + - unmanage the subcloud + - power off the subcloud + - delete the subcloud + - validate that the subcloud is deleted + """ + subcloud_name = "subcloud2" + subcloud_delete(subcloud_name) diff --git a/testcases/cloud_platform/sanity/test_sanity.py b/testcases/cloud_platform/sanity/test_sanity.py index 0ed521b3..68f27919 100644 --- a/testcases/cloud_platform/sanity/test_sanity.py +++ b/testcases/cloud_platform/sanity/test_sanity.py @@ -14,7 +14,6 @@ from framework.ssh.ssh_connection import SSHConnection from framework.validation.validation import validate_equals, validate_equals_with_retry from framework.web.webdriver_core import WebDriverCore from keywords.cloud_platform.dcmanager.dcmanager_alarm_summary_keywords import DcManagerAlarmSummaryKeywords -from keywords.cloud_platform.dcmanager.dcmanager_subcloud_delete_keywords import DcManagerSubcloudDeleteKeywords from keywords.cloud_platform.dcmanager.dcmanager_subcloud_list_keywords import DcManagerSubcloudListKeywords from keywords.cloud_platform.dcmanager.dcmanager_subcloud_manager_keywords import DcManagerSubcloudManagerKeywords from keywords.cloud_platform.dcmanager.dcmanager_subcloud_show_keywords import DcManagerSubcloudShowKeywords @@ -1400,64 +1399,3 @@ def deploy_images_to_local_registry(ssh_connection: SSHConnection): docker_load_image_keywords.load_docker_image_to_host("node-hello-alpine.tar") docker_load_image_keywords.tag_docker_image_for_registry("node-hello:alpine", "node-hello", local_registry) docker_load_image_keywords.push_docker_image_to_registry("node-hello", local_registry) - - -@mark.p0 -@mark.lab_has_min_2_subclouds -def test_dc_subcloud_delete(request): - """ - Verify subcloud deletion works as expected - - Test Steps: - - log onto system controller - - list all subclouds and get the lowest id subcloud - - unmanage the subcloud - - power off the subcloud - - delete the subcloud - - validate that the subcloud is deleted - """ - - get_logger().log_info("Starting 'test_dc_subcloud_delete' test case.") - - # Gets the SSH connection to the active controller of the central cloud. - ssh_connection = LabConnectionKeywords().get_active_controller_ssh() - - # check if subclouds has added in config - subclouds = ConfigurationManager.get_lab_config().get_subclouds() - if not subclouds: - raise ValueError("Failed. No subclouds were found in lab config") - - # List all subclouds and get the lowest id subcloud. - dcm_sc_list_kw = DcManagerSubcloudListKeywords(ssh_connection) - subclouds_list = dcm_sc_list_kw.get_dcmanager_subcloud_list() - - # Retrieves subclouds before deletion. - orig_subclouds = subclouds_list.get_dcmanager_subcloud_list_objects() - total_subclouds = len(orig_subclouds) - lowest_subcloud = subclouds_list.get_healthy_subcloud_with_lowest_id() - sc_name = lowest_subcloud.get_name() - - get_logger().log_info(f"Total subclouds Before deletion: {total_subclouds}") - - msg = (f"Subcloud selected for deletion ID={lowest_subcloud.get_id()} ", f" Name={sc_name}, ", f" Management state={lowest_subcloud.get_management()} ") - get_logger().log_info(msg) - - dcm_sc_manager_kw = DcManagerSubcloudManagerKeywords(ssh_connection) - # Unmanage the subcloud. - get_logger().log_test_case_step(f"Unmanage subcloud={sc_name}.") - dcm_sc_manage_output = dcm_sc_manager_kw.get_dcmanager_subcloud_unmanage(sc_name, timeout=10) - get_logger().log_info(f"The management state of the subcloud {sc_name} was changed to {dcm_sc_manage_output.get_dcmanager_subcloud_manage_object().get_management()}.") - - # poweroff the subcloud. - get_logger().log_test_case_step(f"Poweroff subcloud={sc_name}.") - dcm_sc_manager_kw.set_subcloud_poweroff(sc_name) - # delete the subcloud - get_logger().log_test_case_step(f"Delete subcloud={sc_name}.") - dcm_sc_del_kw = DcManagerSubcloudDeleteKeywords(ssh_connection) - dcm_sc_del_kw.dcmanager_subcloud_delete(sc_name) - # Retrieves subclouds after deletion. - subclouds_list = dcm_sc_list_kw.get_dcmanager_subcloud_list() - subclouds_after_deletion = subclouds_list.get_dcmanager_subcloud_list_objects() - # assert len(subclouds_after_deletion) == total_subclouds - 1, "Subcloud was not deleted successfully" - validate_equals(len(subclouds_after_deletion), total_subclouds - 1, "Subcloud deleted successfully") - validate_equals(subclouds_list.is_subcloud_in_output(sc_name), False, f"{sc_name} is no longer in the subcloud list.")