Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions lisa/microsoft/testsuites/core/vm_resize.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,12 @@ def _verify_vm_resize(
start_stop.stop()
retry = 1
maxretry = 20
expected_vm_capability: Optional[NodeSpace] = None
origin_vm_size: str = ""
final_vm_size: str = ""
last_error: str = ""
while retry < maxretry:
try:
expected_vm_capability: Optional[NodeSpace] = None
expected_vm_capability, origin_vm_size, final_vm_size = resize.resize(
resize_action
)
Expand All @@ -150,14 +153,25 @@ def _verify_vm_resize(
in str(e)
or "Following SKUs have failed for Capacity Restrictions" in str(e)
):
last_error = str(e)
retry = retry + 1
else:
raise e
Comment on lines 158 to 159
time.sleep(1)
finally:
if not hot_resize:
start_stop.start()
assert expected_vm_capability, "fail to find proper vm size"
if not expected_vm_capability:
# The retry loop exhausted without a successful resize. This is an
# environment limitation (no compatible candidate happened to be
# picked across all retries), not a defect in the system under
# test, so surface it as SKIPPED rather than the misleading
# "fail to find proper vm size" assertion.
raise SkippedException(
f"no resize-compatible VM size succeeded after {maxretry - 1} "
f"attempts for action {resize_action}. last Azure error: "
f"{last_error or 'unknown'}"
)

test_result.information["final_vm_size"] = final_vm_size
test_result.information["origin_vm_size"] = origin_vm_size
Expand Down
59 changes: 59 additions & 0 deletions lisa/sut_orchestrator/azure/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -2546,12 +2546,68 @@ def _check_actual_disk_controller_type(

return True

def _get_actual_security_profile(self) -> Optional[SecurityProfileType]:
# Read the security profile the source VM was actually deployed with
# (a concrete value), as opposed to what the SKU advertises in its
# capability SetSpace. Resizing a CVM-deployed VM to a SKU whose
# advertised SetSpace includes CVM AND Standard is fine; resizing
# to a SKU that only advertises Standard will be rejected by Azure
# with PropertyChangeNotAllowed.
Comment on lines +2550 to +2555
if not self._node.capability.features:
return None
for feature in self._node.capability.features:
if feature.type != SecurityProfileSettings.type:
continue
if not isinstance(feature, SecurityProfileSettings):
continue
sp = feature.security_profile
if isinstance(sp, SecurityProfileType):
return sp
if isinstance(sp, search_space.SetSpace):
items = list(sp)
if len(items) == 1:
return items[0]
return None
return None

def _compare_security_profile(
self,
candidate_size: "AzureCapability",
actual_security_profile: Optional[SecurityProfileType],
) -> bool:
# The candidate SKU must support the source VM's deployed security
# profile (CVM, TrustedLaunch/SecureBoot, Stateless, Standard).
# Without this filter, the random selector can pick a non-CVM SKU as
# a resize target for a CVM VM, and every retry will fail with
# PropertyChangeNotAllowed.
if not actual_security_profile:
return True
assert candidate_size.capability
assert candidate_size.capability.features
candidate_sp = next(
(
feature
for feature in candidate_size.capability.features
if feature.type == SecurityProfileSettings.type
),
None,
)
if not isinstance(candidate_sp, SecurityProfileSettings):
return True
Comment on lines +2583 to +2596
cand_profiles = candidate_sp.security_profile
if isinstance(cand_profiles, search_space.SetSpace):
return actual_security_profile in cand_profiles
if isinstance(cand_profiles, SecurityProfileType):
return cand_profiles == actual_security_profile
return True

def _is_candidate_size_valid(
self,
candidate_size: "AzureCapability",
current_vm_size: "AzureCapability",
resize_action: ResizeAction,
actual_disk_controller_type: Optional[schema.DiskControllerType],
actual_security_profile: Optional[SecurityProfileType],
) -> bool:
return (
self._compare_architecture(candidate_size, current_vm_size)
Expand All @@ -2562,6 +2618,7 @@ def _is_candidate_size_valid(
and self._check_actual_disk_controller_type(
candidate_size, actual_disk_controller_type
)
and self._compare_security_profile(candidate_size, actual_security_profile)
and self._compare_size_generation(candidate_size, current_vm_size)
and self._compare_network_interface(candidate_size, current_vm_size)
and self._compare_core_count(candidate_size, current_vm_size, resize_action)
Expand Down Expand Up @@ -2626,6 +2683,7 @@ def _select_vm_size(
assert current_vm_size.capability.features

actual_disk_controller_type = self._get_actual_disk_controller_type()
actual_security_profile = self._get_actual_security_profile()

# Filter candidate vm sizes that can't be resized to
avail_eligible_intersect = [
Expand All @@ -2636,6 +2694,7 @@ def _select_vm_size(
current_vm_size,
resize_action,
actual_disk_controller_type,
actual_security_profile,
)
]

Expand Down
Loading