9494 Images ,
9595)
9696from utilities .data_collector import collect_vnc_screenshot_for_vms
97+ from utilities .exceptions import MigrationStuckSchedulingError , ResourceValueError
9798from utilities .hco import get_hco_namespace , wait_for_hco_conditions
9899from utilities .network import (
99100 cloud_init_network_data ,
@@ -1598,7 +1599,20 @@ def to_dict(self):
15981599 if self .ip_families :
15991600 self .res ["spec" ]["ipFamilies" ] = self .ip_families
16001601
1601- def service_ip (self , ip_family = None ):
1602+ def service_ip (self , admin_client : DynamicClient , ip_family : str | None = None ) -> str :
1603+ """
1604+ Get the IP address of the service.
1605+
1606+ Args:
1607+ admin_client (DynamicClient): admin client to be used for the service.
1608+ ip_family (str | None): IP family to be used for the service.
1609+
1610+ Returns:
1611+ str: IP address of the service.
1612+
1613+ Raises:
1614+ ResourceValueError: If the service IP cannot be retrieved.
1615+ """
16021616 if self .service_type == Service .Type .CLUSTER_IP :
16031617 if ip_family :
16041618 cluster_ips = [
@@ -1611,11 +1625,8 @@ def service_ip(self, ip_family=None):
16111625
16121626 return self .instance .spec .clusterIP
16131627
1614- vm_node = Node (
1615- client = get_client (),
1616- name = self .vmi .instance .status .nodeName ,
1617- )
16181628 if self .service_type == Service .Type .NODE_PORT :
1629+ vm_node = self .vm .vmi .get_node (privileged_client = admin_client )
16191630 if ip_family :
16201631 internal_ips = [
16211632 internal_ip
@@ -1627,6 +1638,10 @@ def service_ip(self, ip_family=None):
16271638
16281639 return self .target_ip or vm_node .internal_ip
16291640
1641+ raise ResourceValueError (
1642+ f"Could not get service IP for service { self .vm .custom_service .name } with type { self .service_type } "
1643+ )
1644+
16301645 @property
16311646 def service_port (self ):
16321647 if self .service_type == Service .Type .CLUSTER_IP :
@@ -1928,7 +1943,7 @@ def migrate_vm_and_verify(
19281943 ) as migration :
19291944 if not wait_for_migration_success :
19301945 return migration
1931- wait_for_migration_finished (namespace = vm . namespace , migration = migration , timeout = timeout )
1946+ wait_for_migration_finished (migration = migration , timeout = timeout )
19321947
19331948 verify_vm_migrated (
19341949 vm = vm ,
@@ -1939,7 +1954,20 @@ def migrate_vm_and_verify(
19391954 return None
19401955
19411956
1942- def wait_for_migration_finished (namespace , migration , timeout = TIMEOUT_12MIN ):
1957+ def wait_for_migration_finished (migration : VirtualMachineInstanceMigration , timeout : int = TIMEOUT_12MIN ) -> None :
1958+ """
1959+ Wait for migration to finish.
1960+ If migration is stuck in Scheduling state, abort the migration and collect data.
1961+
1962+ Args:
1963+ migration (VirtualMachineInstanceMigration): Migration object.
1964+ timeout (int): Maximum time to wait for the migration to finish.
1965+
1966+ Raises:
1967+ MigrationStuckSchedulingError: If the migration is stuck in Scheduling state.
1968+ TimeoutExpiredError: If the migration does not finish within the timeout.
1969+ """
1970+
19431971 sleep = TIMEOUT_10SEC
19441972 samples = TimeoutSampler (wait_timeout = timeout , sleep = sleep , func = lambda : migration .instance .status .phase )
19451973 counter = 0
@@ -1948,36 +1976,43 @@ def wait_for_migration_finished(namespace, migration, timeout=TIMEOUT_12MIN):
19481976 for sample in samples :
19491977 if sample == migration .Status .SUCCEEDED :
19501978 break
1951- elif sample == "Scheduling" :
1979+ if sample == VirtualMachineInstanceMigration . Status . SCHEDULING :
19521980 counter += 1
19531981 # If migration stuck in Scheduling state for more than 4 minutes - most likely it will be failed
19541982 # Need to collect data before 5 min timeout reached and target POD is removed
19551983 if counter >= TIMEOUT_4MIN / sleep :
1956- # Get status/events for PODs in non-running or failed state
1957- for pod in utilities .infra .get_pod_by_name_prefix (
1958- client = get_client (),
1959- pod_prefix = VIRT_LAUNCHER ,
1960- namespace = namespace ,
1961- get_all = True ,
1962- ):
1963- if pod .status not in (Pod .Status .RUNNING , Pod .Status .COMPLETED , Pod .Status .SUCCEEDED ):
1964- pod_events = [
1965- event ["raw_object" ]["message" ]
1966- for event in pod .events (timeout = TIMEOUT_5SEC , field_selector = "type==Warning" )
1967- ]
1968- LOGGER .error (
1969- f"POD Conditions:\n { pod .instance .status .conditions [0 ]} \n "
1970- f"POD Events:\n { ', ' .join (pod_events )} "
1971- )
1972- raise TimeoutExpiredError (
1973- f"VMIM { migration .name } stuck in Scheduling state and probably will be failed"
1974- )
1984+ log_failed_pod_events (migration = migration )
1985+ raise MigrationStuckSchedulingError (migration_name = migration .name )
19751986 except TimeoutExpiredError :
19761987 if sample :
19771988 LOGGER .error (f"Status of VMIM { migration .name } is { sample } " )
19781989 raise
19791990
19801991
1992+ def log_failed_pod_events (migration : VirtualMachineInstanceMigration ) -> None :
1993+ """
1994+ Log failed pod events for a migration.
1995+
1996+ Args:
1997+ migration (VirtualMachineInstanceMigration): Migration object.
1998+ """
1999+
2000+ for pod in utilities .infra .get_pod_by_name_prefix (
2001+ client = migration .client , pod_prefix = VIRT_LAUNCHER , namespace = migration .namespace , get_all = True
2002+ ):
2003+ # Get status/events for PODs in non-running or failed state
2004+ if pod .status not in {Pod .Status .RUNNING , Pod .Status .COMPLETED , Pod .Status .SUCCEEDED }:
2005+ pod_events = [
2006+ event ["raw_object" ]["message" ]
2007+ for event in pod .events (timeout = TIMEOUT_5SEC , field_selector = "type==Warning" )
2008+ ]
2009+ LOGGER .error (
2010+ f"POD Name: { pod .name } \n "
2011+ f"POD Conditions:\n { pod .instance .status .conditions [0 ]} \n "
2012+ f"POD Events:\n { ', ' .join (pod_events )} "
2013+ )
2014+
2015+
19812016def verify_vm_migrated (
19822017 vm ,
19832018 node_before ,
@@ -2331,9 +2366,7 @@ def check_migration_process_after_node_drain(client, vm, admin_client):
23312366 LOGGER .info (f"The VMI was running on { source_node .name } " )
23322367 wait_for_node_schedulable_status (node = source_node , status = False )
23332368 vmim = get_created_migration_job (vm = vm , client = client , timeout = TIMEOUT_5MIN )
2334- wait_for_migration_finished (
2335- namespace = vm .namespace , migration = vmim , timeout = TIMEOUT_30MIN if "windows" in vm .name else TIMEOUT_10MIN
2336- )
2369+ wait_for_migration_finished (migration = vmim , timeout = TIMEOUT_30MIN if "windows" in vm .name else TIMEOUT_10MIN )
23372370
23382371 target_pod = vm .vmi .get_virt_launcher_pod (privileged_client = admin_client )
23392372 target_pod .wait_for_status (status = Pod .Status .RUNNING , timeout = TIMEOUT_3MIN )
0 commit comments