3131import time
3232import traceback
3333import platform
34+ from pathlib import Path
3435from threading import Event
3536from subprocess import call
3637from itertools import groupby
7273if not os .path .exists (path ):
7374 os .mkdir (path )
7475
76+ _failed_reports = []
77+ _log_artifact_dir = os .environ .get ('INTEGRATION_LOG_ARTIFACT_DIR' , 'integration-test-logs' )
78+ _preserved_file_extensions = ('.conf' , '.log' , '.yaml' , '.yml' )
79+
7580cass_version = None
7681cql_version = None
7782
7883
84+ def record_failed_test_report (report ):
85+ if report .failed :
86+ _failed_reports .append (report )
87+
88+
89+ def preserve_ccm_logs_on_failure (cluster_name = None ):
90+ if not _failed_reports :
91+ return
92+
93+ ccm_root = Path (path )
94+ if cluster_name :
95+ ccm_root = ccm_root / cluster_name
96+ if not ccm_root .is_dir ():
97+ return
98+
99+ destination = Path (_log_artifact_dir ).resolve ()
100+ ccm_destination = destination / 'ccm'
101+ if cluster_name :
102+ ccm_destination = ccm_destination / cluster_name
103+ ccm_destination .mkdir (parents = True , exist_ok = True )
104+
105+ with (destination / 'failed-tests.txt' ).open ('w' ) as failed_tests :
106+ for report in _failed_reports :
107+ failed_tests .write ('%s %s\n ' % (report .when , report .nodeid ))
108+
109+ # Preserve CCM logs/configs before cleanup removes the cluster directories.
110+ for source_file in ccm_root .rglob ('*' ):
111+ if not source_file .is_file ():
112+ continue
113+
114+ rel_file = source_file .relative_to (ccm_root )
115+ preserve_all = bool ({'conf' , 'logs' }.intersection (rel_file .parts ))
116+ if not preserve_all and not source_file .name .endswith (_preserved_file_extensions ):
117+ continue
118+
119+ destination_file = ccm_destination / rel_file
120+ try :
121+ destination_file .parent .mkdir (parents = True , exist_ok = True )
122+ shutil .copy2 (source_file , destination_file )
123+ except OSError as exc :
124+ log .warning ("Failed to preserve file %s: %s" , source_file , exc )
125+
126+ log .info ("Preserved ccm logs for failed tests under %s" , destination )
127+
128+
79129def get_server_versions ():
80130 """
81131 Probe system.local table to determine Cassandra and CQL version.
@@ -383,6 +433,7 @@ def remove_cluster():
383433 tries = 0
384434 while tries < 100 :
385435 try :
436+ preserve_ccm_logs_on_failure (CCM_CLUSTER .name )
386437 CCM_CLUSTER .remove ()
387438 CCM_CLUSTER = None
388439 return
@@ -455,6 +506,7 @@ def use_cluster(cluster_name, nodes, ipformat=None, start=True, workloads=None,
455506 try :
456507 CCM_CLUSTER = CCMClusterFactory .load (path , cluster_name )
457508 log .debug ("Found existing CCM cluster, {0}; clearing." .format (cluster_name ))
509+ preserve_ccm_logs_on_failure (cluster_name )
458510 CCM_CLUSTER .clear ()
459511 CCM_CLUSTER .set_install_dir (** ccm_options )
460512 CCM_CLUSTER .set_configuration_options (configuration_options )
@@ -471,6 +523,7 @@ def use_cluster(cluster_name, nodes, ipformat=None, start=True, workloads=None,
471523 # Make sure we cleanup old cluster dir if it exists
472524 cluster_path = os .path .join (path , cluster_name )
473525 if os .path .exists (cluster_path ):
526+ preserve_ccm_logs_on_failure (cluster_name )
474527 shutil .rmtree (cluster_path )
475528
476529 if SCYLLA_VERSION :
@@ -553,6 +606,7 @@ def teardown_package():
553606 return
554607 # when multiple modules are run explicitly, this runs between them
555608 # need to make sure CCM_CLUSTER is properly cleared for that case
609+ preserve_ccm_logs_on_failure ()
556610 remove_cluster ()
557611 for cluster_name in [CLUSTER_NAME , MULTIDC_CLUSTER_NAME ]:
558612 try :
0 commit comments