Skip to content

Commit ccb3e4f

Browse files
lmicciniclaude
andcommitted
Remove dead code and consolidate redundant patterns
Remove MigrationStatus enum (inline strings), dead _previous_hash state, unreachable VALIDATION_PATTERNS entries, unused _check_critical_services params, redundant validate_input in _host_fence, single-use _cleanup_dict_by_condition helper, and redundant requests.exceptions import. Consolidate 5 manual error+traceback log pairs into _safe_log_exception calls. Deduplicate _get_nova_connection by delegating to _establish_nova_connection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c492746 commit ccb3e4f

3 files changed

Lines changed: 20 additions & 68 deletions

File tree

templates/instanceha/bin/instanceha.py

Lines changed: 16 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from enum import Enum
1313
import concurrent.futures
1414
import requests
15-
import requests.exceptions
1615
import yaml
1716
import subprocess
1817
from yaml.loader import SafeLoader
@@ -179,18 +178,9 @@ class MatchType(Enum):
179178
ZONE = "zone"
180179

181180

182-
class MigrationStatus(Enum):
183-
"""Migration status values."""
184-
COMPLETED = "completed"
185-
DONE = "done"
186-
ERROR = "error"
187-
FAILED = "failed"
188-
CANCELLED = "cancelled"
189-
190-
191181
# Migration status constants for checking migration state
192-
MIGRATION_STATUS_COMPLETED = [MigrationStatus.COMPLETED.value, MigrationStatus.DONE.value]
193-
MIGRATION_STATUS_ERROR = [MigrationStatus.ERROR.value, MigrationStatus.FAILED.value, MigrationStatus.CANCELLED.value]
182+
MIGRATION_STATUS_COMPLETED = ["completed", "done"]
183+
MIGRATION_STATUS_ERROR = ["error", "failed", "cancelled"]
194184

195185

196186
# Dataclasses
@@ -312,8 +302,6 @@ def _retry_with_backoff(func, max_attempts, label, delay=FENCING_RETRY_DELAY_SEC
312302
'power_action': (['on', 'off', 'status', 'reset', 'cycle', 'soft', 'On', 'ForceOff',
313303
'GracefulShutdown', 'GracefulRestart', 'ForceRestart', 'Nmi',
314304
'ForceOn', 'PushPowerButton'], None),
315-
'ip_address': ('ip', None), # Special marker for IP address validation
316-
'port': ('port', None), # Special marker for port validation
317305
'username': (r'^[a-zA-Z0-9_-]{1,64}$', USERNAME_MAX_LENGTH),
318306
'hostname': (r'^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$', USERNAME_MAX_LENGTH)
319307
}
@@ -768,7 +756,6 @@ def __init__(self, config_manager: ConfigManager, cloud_client: Optional[OpenSta
768756
self.current_hash = ""
769757
self.hash_update_successful = False
770758
self._last_hash_time = 0
771-
self._previous_hash = ""
772759
self.ready = False
773760
self.k8s_api_reachable = False
774761

@@ -838,7 +825,6 @@ def update_health_hash(self, hash_interval: Optional[int] = None) -> None:
838825
if current_timestamp - self._last_hash_time > hash_interval:
839826
self.current_hash = hashlib.sha256(str(current_timestamp).encode()).hexdigest()
840827
self.hash_update_successful = True
841-
self._previous_hash = self.current_hash
842828
self._last_hash_time = current_timestamp
843829

844830
def get_connection(self) -> Optional[OpenStackClient]:
@@ -953,8 +939,7 @@ def _get_cached_evacuable(self, cache_attr: str, config_key: str,
953939
try:
954940
cache_data = fetch_func(connection)
955941
except Exception as e:
956-
logging.error("Failed to get evacuable resources (%s): %s", config_key, e)
957-
logging.debug('Exception traceback:', exc_info=True)
942+
_safe_log_exception(f"Failed to get evacuable resources ({config_key})", e, include_traceback=True)
958943
cache_data = []
959944
with self._cache_lock:
960945
setattr(self, cache_attr, cache_data)
@@ -1115,8 +1100,7 @@ def is_aggregate_evacuable(self, connection: OpenStackClient, host: str, aggrega
11151100
return any(host in agg.hosts for agg in aggregates
11161101
if self._is_resource_evacuable(agg, self.evacuable_tag, ['metadata']))
11171102
except Exception as e:
1118-
logging.error("Failed to check aggregate evacuability for %s: %s", host, e)
1119-
logging.debug('Exception traceback:', exc_info=True)
1103+
_safe_log_exception(f"Failed to check aggregate evacuability for {host}", e, include_traceback=True)
11201104
return False
11211105

11221106
def get_hosts_with_servers_cached(self, connection, services):
@@ -1290,15 +1274,7 @@ def do_GET(self):
12901274
except Exception as e:
12911275
logging.error('Health check server failed: %s', e)
12921276

1293-
def _cleanup_dict_by_condition(self, dictionary: dict, condition_func: Callable[[Any, Any], bool],
1294-
log_message: Optional[str] = None) -> int:
1295-
"""Remove dictionary entries matching condition function."""
1296-
to_remove = [k for k, v in dictionary.items() if condition_func(k, v)]
1297-
for k in to_remove:
1298-
del dictionary[k]
1299-
if log_message:
1300-
logging.debug(log_message.format(k))
1301-
return len(to_remove)
1277+
13021278

13031279
def refresh_evacuable_cache(self, connection=None, force=False, cache_timeout=CACHE_TIMEOUT_SECONDS):
13041280
"""Refresh evacuable flavors and images cache with intelligent timing."""
@@ -1551,8 +1527,7 @@ def _update_service_disable_reason(connection, host, service_id=None) -> bool:
15511527
logging.debug('Updated disabled reason for host %s after evacuation failure', host)
15521528
return True
15531529
except Exception as e:
1554-
logging.error('Failed to update disable_reason for host %s. Error: %s', host, e)
1555-
logging.debug('Exception traceback:', exc_info=True)
1530+
_safe_log_exception(f"Failed to update disable_reason for host {host}", e, include_traceback=True)
15561531
return False
15571532

15581533

@@ -1899,9 +1874,7 @@ def _monitor_evacuation(connection, server_id, response_uuid, start_time,
18991874

19001875
except Exception as e:
19011876
api_error_count += 1
1902-
logging.error("Error checking evacuation status for %s: %s",
1903-
response_uuid, _sanitize_message(str(e)))
1904-
logging.debug('Exception traceback:', exc_info=True)
1877+
_safe_log_exception(f"Error checking evacuation status for {response_uuid}", e, include_traceback=True)
19051878
if api_error_count >= MAX_API_RETRIES:
19061879
logging.error("Too many API errors checking evacuation status for %s. Giving up.",
19071880
response_uuid)
@@ -1993,9 +1966,7 @@ def _server_evacuate_future(connection, server, target_host=None,
19931966
return result
19941967

19951968
except Exception as e:
1996-
logging.error("Unexpected error during evacuation of server %s: %s",
1997-
server.id, _sanitize_message(str(e)))
1998-
logging.debug('Exception traceback:', exc_info=True)
1969+
_safe_log_exception(f"Unexpected error during evacuation of server {server.id}", e, include_traceback=True)
19991970
_emit_k8s_event(source_host, 'InstanceEvacuationFailed',
20001971
f'Instance {server.id} evacuation error: {_sanitize_message(str(e))}',
20011972
event_type='Warning')
@@ -2520,12 +2491,6 @@ def _host_fence(host, action, service):
25202491
logging.error("Invalid fence parameters: missing host")
25212492
return False
25222493

2523-
# Validate action parameter at entry point
2524-
if not validate_input(action, 'power_action', "host fencing"):
2525-
logging.error("Invalid fence action: %s", action)
2526-
return False
2527-
2528-
# Additional check for fence-specific actions
25292494
if action not in ['on', 'off']:
25302495
logging.error("Unsupported fence action: %s (only 'on' and 'off' supported)", action)
25312496
return False
@@ -2558,14 +2523,7 @@ def _host_fence(host, action, service):
25582523

25592524
def _get_nova_connection(service):
25602525
"""Establish a connection to Nova using service configuration."""
2561-
try:
2562-
return service.create_connection()
2563-
except NovaConnectionError:
2564-
logging.error("Nova connection failed")
2565-
return None
2566-
except Exception as e:
2567-
_safe_log_exception("Failed to establish Nova connection", e)
2568-
return None
2526+
return _establish_nova_connection(service, fatal=False)
25692527

25702528

25712529
def _enable_matching_reserved_host(conn, failed_service, reserved_hosts, service,
@@ -3019,7 +2977,7 @@ def _categorize_services(services: List[Any], target_date: datetime) -> tuple:
30192977

30202978
return compute_nodes, resume, reenable
30212979

3022-
def _check_critical_services(conn, services, compute_nodes):
2980+
def _check_critical_services(services):
30232981
"""Check if critical Nova services are operational for evacuation."""
30242982
# Check if at least one scheduler is up
30252983
schedulers = [s for s in services if s.binary == 'nova-scheduler']
@@ -3048,10 +3006,11 @@ def _filter_processing_hosts(service, compute_nodes, to_resume):
30483006

30493007
with service.processing_lock:
30503008
# Clean up expired processing entries
3051-
service._cleanup_dict_by_condition(
3052-
service.hosts_processing,
3053-
lambda h, t: current_time - t > max_processing_time + MAX_PROCESSING_TIME_PADDING_SECONDS,
3054-
'Cleaned up expired processing entry for {}')
3009+
expired = [h for h, t in service.hosts_processing.items()
3010+
if current_time - t > max_processing_time + MAX_PROCESSING_TIME_PADDING_SECONDS]
3011+
for h in expired:
3012+
del service.hosts_processing[h]
3013+
logging.debug('Cleaned up expired processing entry for %s', h)
30553014

30563015
# Filter out hosts currently being processed
30573016
original_count = len(compute_nodes)
@@ -3278,7 +3237,7 @@ def _process_stale_services(conn, service, services, compute_nodes, to_resume):
32783237
# Process evacuations
32793238
if not service.config.get_config_value('DISABLED'):
32803239
# Check if critical services are operational
3281-
can_evacuate, error_msg = _check_critical_services(conn, services, compute_nodes)
3240+
can_evacuate, error_msg = _check_critical_services(services)
32823241
if not can_evacuate:
32833242
logging.error('Cannot evacuate: %s. Skipping evacuation.', error_msg)
32843243
_cleanup_filtered_hosts(service, marked_hostnames, set(), current_time)

test/instanceha/test_config_features.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -711,37 +711,31 @@ def test_all_schedulers_down(self):
711711
]
712712
compute_nodes = [services[2]]
713713

714-
can_evacuate, error_msg = instanceha._check_critical_services(mock_conn, services, compute_nodes)
714+
can_evacuate, error_msg = instanceha._check_critical_services(services)
715715

716716
self.assertFalse(can_evacuate)
717717
self.assertIn('nova-scheduler', error_msg)
718718

719719
def test_at_least_one_scheduler_up(self):
720720
"""Test that check passes when at least one scheduler is up."""
721-
mock_conn = Mock()
722-
723721
services = [
724722
Mock(binary='nova-scheduler', state='down', host='controller-1'),
725723
Mock(binary='nova-scheduler', state='up', host='controller-2'),
726724
Mock(binary='nova-compute', state='down', host='compute-1')
727725
]
728-
compute_nodes = [services[2]]
729726

730-
can_evacuate, error_msg = instanceha._check_critical_services(mock_conn, services, compute_nodes)
727+
can_evacuate, error_msg = instanceha._check_critical_services(services)
731728

732729
self.assertTrue(can_evacuate)
733730
self.assertEqual(error_msg, "")
734731

735732
def test_no_schedulers_defined(self):
736733
"""Test when no schedulers are in services list."""
737-
mock_conn = Mock()
738-
739734
services = [
740735
Mock(binary='nova-compute', state='down', host='compute-1')
741736
]
742-
compute_nodes = [services[0]]
743737

744-
can_evacuate, error_msg = instanceha._check_critical_services(mock_conn, services, compute_nodes)
738+
can_evacuate, error_msg = instanceha._check_critical_services(services)
745739

746740
# Should allow evacuation if no schedulers are in the list
747741
self.assertTrue(can_evacuate)

test/instanceha/test_coverage_gaps.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,6 @@ def _make_service(self):
912912
svc.refresh_evacuable_cache = Mock()
913913
svc.get_hosts_with_servers_cached = Mock(return_value={})
914914
svc.filter_hosts_with_servers = Mock(return_value=[])
915-
svc._cleanup_dict_by_condition = Mock()
916915
return svc
917916

918917
def test_empty_compute_nodes_and_resume(self):
@@ -939,7 +938,7 @@ def test_threshold_exceeded_skips_evacuation(self):
939938
service.get_hosts_with_servers_cached.return_value = {'host-1': ['server-1']}
940939
service.filter_hosts_with_servers.return_value = [svc1]
941940
with patch('instanceha._emit_k8s_event'):
942-
with patch('instanceha._check_critical_services', return_value=(True, None)):
941+
with patch('instanceha._check_critical_services', return_value=(True, "")):
943942
instanceha._process_stale_services(conn, service, [svc1], [svc1], [])
944943

945944
def test_disabled_mode_skips_evacuation(self):

0 commit comments

Comments
 (0)