Add negative testcases and update positive testcase for subcloud rename

Negative testcases were added to validate subcloud rename failures with
invalid inputs (same name, existing name, invalid name). The positive
testcase was also modified to simplify the helper function and return
the original subcloud name for consistent teardown handling.

Test Plan:
PASS: Verify valid subcloud rename succeeds and restores original name
PASS: Verify renaming to the same subcloud name fails with correct error
PASS: Verify renaming to an existing subcloud name fails with correct error
PASS: Verify renaming with invalid subcloud name fails with correct error

Change-Id: Iba755883f10b9726d814dc69d1f145466a2e133a
Signed-off-by: aabhinav <ayyapasetti.abhinav@windriver.com>
This commit is contained in:
aabhinav
2025-09-22 10:43:11 -04:00
parent 6c919f4871
commit d891c4a2c9
3 changed files with 245 additions and 45 deletions

View File

@@ -1,3 +1,4 @@
from framework.ssh.ssh_connection import SSHConnection
from keywords.base_keyword import BaseKeyword
from keywords.cloud_platform.command_wrappers import source_openrc
from keywords.cloud_platform.dcmanager.objects.dcmanager_subcloud_show_output import DcManagerSubcloudShowOutput
@@ -8,11 +9,12 @@ class DcManagerSubcloudUpdateKeywords(BaseKeyword):
This class contains all the keywords related to the 'dcmanager subcloud update' commands.
"""
def __init__(self, ssh_connection):
def __init__(self, ssh_connection: SSHConnection):
"""
Constructor
Args:
ssh_connection:
ssh_connection(SSHConnection): An active SSH connection to the controller.
"""
self.ssh_connection = ssh_connection
@@ -20,11 +22,14 @@ class DcManagerSubcloudUpdateKeywords(BaseKeyword):
def dcmanager_subcloud_update(self, subcloud_name: str, update_attr: str, update_value: str) -> DcManagerSubcloudShowOutput:
"""
Updates the subcloud attr using'dcmanager subcloud update <subcloud name> --<update_attr> <update_value>' output.
Args: subcloud_name (str): a str representing a subcloud's name.
update_attr (str): the attribute to update (ex. description)
update_value (str): the value to update the attribute to (ex. this is a new description)
Returns: DcManagerSubcloudShowOutput
Args:
subcloud_name (str): a str representing a subcloud's name.
update_attr (str): the attribute to update (ex. description)
update_value (str): the value to update the attribute to (ex. this is a new description)
Returns:
DcManagerSubcloudShowOutput: Object representation of the command output.
"""
output = self.ssh_connection.send(source_openrc(f"dcmanager subcloud update {subcloud_name} --{update_attr} '{update_value}'"))
@@ -32,3 +37,21 @@ class DcManagerSubcloudUpdateKeywords(BaseKeyword):
dcmanager_subcloud_show_output = DcManagerSubcloudShowOutput(output)
return dcmanager_subcloud_show_output
def dcmanager_subcloud_update_with_error(self, subcloud_name: str, update_attr: str, update_value: str) -> str:
"""
Updates the subcloud attr using'dcmanager subcloud update <subcloud name> --<update_attr> <update_value>' output(error handling version).
Args:
subcloud_name (str): a str representing a subcloud's name.
update_attr (str): the attribute to update (ex. description)
update_value (str): the value to update the attribute to (ex. this is a new description)
Returns:
str: Raw command output (for error validation).
"""
output = self.ssh_connection.send(source_openrc(f"dcmanager subcloud update {subcloud_name} --{update_attr} '{update_value}'"))
if isinstance(output, list) and len(output) > 0:
return "\n".join(line.strip() for line in output)
return output.strip() if isinstance(output, str) else str(output)

View File

@@ -149,6 +149,30 @@ class DcManagerSubcloudListOutput:
lowest_subcloud = min(subclouds, key=lambda subcloud: int(subcloud.get_id()))
return lowest_subcloud
def get_different_healthy_subcloud(self, exclude_subcloud_name: str) -> DcManagerSubcloudListObject:
"""
Gets a healthy subcloud whose name is different from the argument 'exclude_subcloud_name'.
Args:
exclude_subcloud_name (str): The name of the subcloud to exclude.
Returns:
DcManagerSubcloudListObject: An instance of DcManagerSubcloudListObject representing a healthy subcloud
(Managed, Online, Deploy completed, Synchronized) whose name is different from 'exclude_subcloud_name'.
Raises:
ValueError: If no eligible healthy subcloud is found excluding 'exclude_subcloud_name'.
"""
dcmanager_subcloud_list_object_filter = DcManagerSubcloudListObjectFilter.get_healthy_subcloud_filter()
subclouds = self.get_dcmanager_subcloud_list_objects_filtered(dcmanager_subcloud_list_object_filter)
eligible_subclouds = [sc for sc in subclouds if sc.get_name() != exclude_subcloud_name]
if not eligible_subclouds:
error_message = f"No healthy subcloud found excluding '{exclude_subcloud_name}'."
raise ValueError(error_message)
return min(eligible_subclouds, key=lambda sc: int(sc.get_id()))
def get_subcloud_by_name(self, subcloud_name: str) -> DcManagerSubcloudListObject:
"""
Gets an instance of DcManagerSubcloudListObject whose name attribute is equal to the argument 'subcloud_name'.

View File

@@ -1,65 +1,218 @@
import time
from typing import Tuple
from pytest import mark
from framework.kpi.time_kpi import TimeKPI
from framework.logging.automation_logger import get_logger
from framework.validation.validation import validate_equals
from framework.ssh.ssh_connection import SSHConnection
from framework.validation.validation import validate_equals, validate_str_contains
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_update_keywords import DcManagerSubcloudUpdateKeywords
from keywords.cloud_platform.health.health_keywords import HealthKeywords
from keywords.cloud_platform.ssh.lab_connection_keywords import LabConnectionKeywords
def get_subcloud_name(ssh_connection: SSHConnection) -> str:
"""
Retrieve the name of the healthy subcloud with the lowest ID.
Args:
ssh_connection (SSHConnection): Active SSH connection to the controller.
Returns:
str: The original subcloud name.
"""
dcm_sc_list_kw = DcManagerSubcloudListKeywords(ssh_connection)
lowest_subcloud = dcm_sc_list_kw.get_dcmanager_subcloud_list().get_healthy_subcloud_with_lowest_id()
sc_name_original = lowest_subcloud.get_name()
return sc_name_original
def dcmanager_subcloud_rename(ssh_connection: SSHConnection, sc_name_new: str) -> str:
"""
Perform a successful subcloud rename.
This function:
- Retrieves the original subcloud name.
- Unmanages the subcloud.
- Renames it using dcmanager.
- Returns the original subcloud name for teardown.
Args:
ssh_connection (SSHConnection): Active SSH connection to the controller.
sc_name_new (str): The new name for the subcloud.
Returns:
str: The original subcloud name.
"""
sc_name_original = get_subcloud_name(ssh_connection)
dcm_sc_kw = DcManagerSubcloudManagerKeywords(ssh_connection)
dcm_sc_kw.get_dcmanager_subcloud_unmanage(sc_name_original, 30)
get_logger().log_info(f"Subcloud selected for rename: {sc_name_original}")
DcManagerSubcloudUpdateKeywords(ssh_connection).dcmanager_subcloud_update(sc_name_original, "name", sc_name_new)
get_logger().log_info(f"Subcloud {sc_name_original} renamed to {sc_name_new}")
return sc_name_original
def dcmanager_subcloud_rename_negative(ssh_connection: SSHConnection, sc_name_new: str) -> Tuple[str, str]:
"""
Attempt a subcloud rename expected to fail.
This function:
- Retrieves the original subcloud name.
- Unmanages the subcloud.
- Attempts rename with invalid/new name.
- Returns error message and original subcloud name.
Args:
ssh_connection (SSHConnection): Active SSH connection to the controller.
sc_name_new (str): The new (invalid or conflicting) name for the subcloud.
Returns:
Tuple[str, str]: (error_message, original subcloud name)
"""
sc_name_original = get_subcloud_name(ssh_connection)
dcm_sc_kw = DcManagerSubcloudManagerKeywords(ssh_connection)
dcm_sc_kw.get_dcmanager_subcloud_unmanage(sc_name_original, 30)
get_logger().log_info(f"Subcloud selected for rename: {sc_name_original}")
error_message = DcManagerSubcloudUpdateKeywords(ssh_connection).dcmanager_subcloud_update_with_error(sc_name_original, "name", sc_name_new)
return error_message, sc_name_original
@mark.p0
@mark.lab_has_subcloud
def test_dc_subcloud_rename(request):
def test_subcloud_rename(request):
"""
Verify subcloud rename
Test Steps:
- log onto active controller
- Get original name
- Run dcmanager subcloud update <subcloud_name> --name <new_name>
- validate that subcloud has new name
- log the time kpi takes from out-of-sync to in-sync
- Reset name back to old name
- Log onto the active controller
- Get the original subcloud name
- Rename subcloud
- Manage the renamed subcloud
- Validate that the subcloud has the new name
- Validate that the subcloud returns to "in-sync" state
- Reset subcloud name back to original
"""
ssh_connection = LabConnectionKeywords().get_active_controller_ssh()
# new name used for renaming
sc_name_new = "testsubcloudrename"
# Get the lowest subcloud (the subcloud with the lowest id).
dcm_sc_list_kw = DcManagerSubcloudListKeywords(ssh_connection)
lowest_subcloud = dcm_sc_list_kw.get_dcmanager_subcloud_list().get_healthy_subcloud_with_lowest_id()
sc_name_original = lowest_subcloud.get_name()
get_logger().log_info(f"Subcloud selected for rename: {sc_name_original}")
# unmange before rename
dcm_sc_kw = DcManagerSubcloudManagerKeywords(ssh_connection)
dcm_sc_manage_out = dcm_sc_kw.get_dcmanager_subcloud_unmanage(sc_name_original, 300)
get_logger().log_info(f"The management state of the subcloud {sc_name_original} was changed to {dcm_sc_manage_out.get_dcmanager_subcloud_manage_object().get_management()}.")
time_kpi = TimeKPI(time.time())
subcloud_update_output = DcManagerSubcloudUpdateKeywords(ssh_connection).dcmanager_subcloud_update(sc_name_original, "name", sc_name_new)
get_logger().log_info(f"sc rename: {subcloud_update_output.get_dcmanager_subcloud_show_object().__dict__}")
dcm_sc_manage_out = dcm_sc_kw.get_dcmanager_subcloud_manage(sc_name_new, 300)
sc_name_new = "testsubcloudrenames"
sc_name_original = dcmanager_subcloud_rename(ssh_connection, sc_name_new)
def teardown():
dcm_sc_kw.get_dcmanager_subcloud_unmanage(sc_name_new, 300)
DcManagerSubcloudUpdateKeywords(ssh_connection).dcmanager_subcloud_update(sc_name_new, "name", sc_name_original)
# manage the cloud back
dcm_sc_kw.get_dcmanager_subcloud_manage(sc_name_original, 300)
# Register the teardown function to be called after the test
request.addfinalizer(teardown)
# Manage renamed subcloud
dcm_sc_kw.get_dcmanager_subcloud_manage(sc_name_new, 30)
# Validate rename
obj_subcloud = dcm_sc_list_kw.get_dcmanager_subcloud_list().get_subcloud_by_name(sc_name_new)
validate_equals(obj_subcloud.get_name(), sc_name_new, "Validate that the name has been changed")
# Validate that subcloud is in Insync Status
# Validate in-sync
dcm_sc_list_kw.validate_subcloud_sync_status(sc_name_new, "in-sync")
time_kpi.log_elapsed_time(time.time(), "time taken for subcloud In-Sync")
# validate Healthy status
# since the lab_config does not have the renamed subcloud name, using the original name to ssh
subcloud_ssh = LabConnectionKeywords().get_subcloud_ssh(sc_name_original)
HealthKeywords(subcloud_ssh).validate_healty_cluster()
# Teardown to reset subcloud name
def teardown():
dcmanager_subcloud_rename(ssh_connection, sc_name_original)
dcm_sc_kw.get_dcmanager_subcloud_manage(sc_name_original, 300)
dcm_sc_list_kw.validate_subcloud_sync_status(sc_name_original, "in-sync")
request.addfinalizer(teardown)
@mark.p0
@mark.lab_has_subcloud
def test_subcloud_rename_negative_same_subcloud_name(request) -> None:
"""
Verify that renaming a subcloud to its existing name fails with the correct error.
Test Steps:
- Log onto the active controller
- Get the current subcloud name
- Attempt to rename the subcloud with the same name
- Validate that the error message indicates renaming to the same name is not allowed
- Ensure subcloud is managed again after the test
"""
ssh_connection = LabConnectionKeywords().get_active_controller_ssh()
dcm_sc_list_kw = DcManagerSubcloudListKeywords(ssh_connection)
sc_name_new = get_subcloud_name(ssh_connection)
# Attempt invalid rename (same name)
error_message, sc_name_original = dcmanager_subcloud_rename_negative(ssh_connection, sc_name_new)
# Validate error message
validate_str_contains(error_message, "same as the current subcloud", f"Subcloud rename should not allow a name that is the same as the current name {sc_name_new}")
def teardown():
dcm_sc_kw = DcManagerSubcloudManagerKeywords(ssh_connection)
dcm_sc_kw.get_dcmanager_subcloud_manage(sc_name_original, timeout=60)
dcm_sc_list_kw.validate_subcloud_sync_status(sc_name_original, "in-sync")
request.addfinalizer(teardown)
@mark.p0
@mark.lab_has_min_2_subclouds
def test_dcmanager_subcloud_rename_negative_existing_subcloud_name(request) -> None:
"""
Verify that attempting to rename a subcloud to the name of another existing subcloud fails.
Test Steps:
- Log into the active controller.
- Retrieve the name of the subcloud to be renamed.
- Identify a different healthy subcloud to use its name for the rename attempt.
- Attempt to rename the original subcloud using the existing name of another subcloud.
- Validate that the error message indicates that the name is already in use.
- Ensure the original subcloud is managed and in-sync after the test.
"""
ssh_connection = LabConnectionKeywords().get_active_controller_ssh()
dcm_sc_list_kw = DcManagerSubcloudListKeywords(ssh_connection)
sc_name = get_subcloud_name(ssh_connection)
sc_name_new = dcm_sc_list_kw.get_dcmanager_subcloud_list().get_different_healthy_subcloud(sc_name).get_name()
# Attempt invalid rename (existing subcloud name)
error_message, sc_name_original = dcmanager_subcloud_rename_negative(ssh_connection, sc_name_new)
# Validate error message
validate_str_contains(error_message, "already exist", f"Subcloud name should not be the same as another existing subcloud {sc_name_new}")
def teardown():
dcm_sc_kw = DcManagerSubcloudManagerKeywords(ssh_connection)
dcm_sc_kw.get_dcmanager_subcloud_manage(sc_name_original, timeout=60)
dcm_sc_list_kw.validate_subcloud_sync_status(sc_name_original, "in-sync")
request.addfinalizer(teardown)
@mark.p0
@mark.lab_has_subcloud
def test_dcmanager_subcloud_rename_negative_invalid_name(request) -> None:
"""
Verify that renaming a subcloud with an invalid name fails.
Test Steps:
- Log onto the active controller
- Attempt to rename a subcloud with an invalid name
- Validate that the error message indicates the name is invalid
- Ensure subcloud is managed again after the test
"""
ssh_connection = LabConnectionKeywords().get_active_controller_ssh()
sc_name_new = "Subcloud" # Invalid name because subcloud names should not consist of uppercase letters
# Attempt invalid rename
error_message, sc_name_original = dcmanager_subcloud_rename_negative(ssh_connection, sc_name_new)
# Validate expected error message
validate_str_contains(error_message, "must contain alphabetic characters", f"Error message for invalid subcloud name: {sc_name_new}")
def teardown():
dcm_sc_list_kw = DcManagerSubcloudListKeywords(ssh_connection)
dcm_sc_kw = DcManagerSubcloudManagerKeywords(ssh_connection)
dcm_sc_kw.get_dcmanager_subcloud_manage(sc_name_original, timeout=60)
dcm_sc_list_kw.validate_subcloud_sync_status(sc_name_original, "in-sync")
request.addfinalizer(teardown)