diff --git a/ceph_devstack/__init__.py b/ceph_devstack/__init__.py index fc0cc8d9..ca2267fc 100644 --- a/ceph_devstack/__init__.py +++ b/ceph_devstack/__init__.py @@ -90,6 +90,15 @@ def parse_args(args: List[str]) -> argparse.Namespace: default=False, help="Leave the cluster running - and don't auto-schedule anything", ) + + parser_show_log = subparsers.add_parser("show-log", help="Show test logs") + parser_show_log.add_argument( + "-f", + "--filepath", + action="store_true", + default=False, + help="Output just the full path of the logfile instead of its content" + ) subparsers.add_parser("remove", help="Destroy the cluster") subparsers.add_parser("start", help="Start the cluster") subparsers.add_parser("stop", help="Stop the cluster") diff --git a/ceph_devstack/cli.py b/ceph_devstack/cli.py index ccb02ef8..04f95a4b 100644 --- a/ceph_devstack/cli.py +++ b/ceph_devstack/cli.py @@ -40,6 +40,8 @@ async def run(): return elif args.command == "wait": return await obj.wait(container_name=args.container) + elif args.command == "show-log": + return await obj.show_log(filepath=args.filepath) else: await obj.apply(args.command) return 0 diff --git a/ceph_devstack/resources/ceph/__init__.py b/ceph_devstack/resources/ceph/__init__.py index 344cc16a..c5f1cd6f 100644 --- a/ceph_devstack/resources/ceph/__init__.py +++ b/ceph_devstack/resources/ceph/__init__.py @@ -2,6 +2,7 @@ import contextlib import os import tempfile +from datetime import datetime from subprocess import CalledProcessError @@ -226,3 +227,68 @@ async def wait(self, container_name: str): return await object.wait() logger.error(f"Could not find container {container_name}") return 1 + + async def show_log(self, filepath: bool = False): + home_dir = os.path.expanduser("~") + target_dir = os.path.join(home_dir, ".local", "share", "ceph-devstack", "archive") + run_directories_list = None + if os.path.exists(target_dir) and os.path.isdir(target_dir): + run_directories_list = os.listdir(target_dir) + else: + logger.error(f"Error: {target_dir} does not exist or is not a directory") + return 1 + def extract_timestamp(dir_name): + try: + parts = dir_name.split("-") + year, month, day_time = parts[1], parts[2], parts[3] + day, time = day_time.split("_") + timestamp = f"{year}-{month}-{day} {time}" + return datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S") + except Exception as e: + logger.error(f"Error extracting timestamp from {dir_name}: {e}") + return None + run_directories_list.sort(key=lambda dir: extract_timestamp(dir), reverse=True) + latest_directory = run_directories_list[0] if run_directories_list else None + try: + latest_directory = os.path.join(target_dir, latest_directory) + all_job_ids = [jobID for jobID in os.listdir(latest_directory) if os.path.isdir(os.path.join(latest_directory, jobID)) and jobID.isdigit()] + if not all_job_ids: + logger.info("No latest jobIDs") # We could add a feature to go to the "next" latest directory + else: + if(len(all_job_ids)>1): + print("ALERT: Multiple jobIDs found: ", ", ".join(all_job_ids)) + while True: + selected_jobID = input("Select jobID to view corresponding log: ") + if selected_jobID in all_job_ids: + log_file_path = os.path.join(latest_directory, selected_jobID, "teuthology.log") + if os.path.exists(log_file_path) and os.path.isfile(log_file_path) and not filepath: + print("<-----------teuthology.log contents----------->") + with open(log_file_path, "r") as file: + print(file.read()) + return 0 + elif filepath: + logger.info(f"{log_file_path}") + return 0 + else: + logger.error(f"Error: teuthology.log file not found in {os.path.join(latest_directory, selected_jobID)}") + return 1 + else: + logger.error(f"Error: {selected_jobID} is not a valid jobID") + else: + selected_jobID = all_job_ids[0] + log_file_path = os.path.join(latest_directory, selected_jobID, "teuthology.log") + if os.path.exists(log_file_path) and os.path.isfile(log_file_path) and not filepath: + print("<-----------teuthology.log contents----------->") + with open(log_file_path, "r") as file: + print(file.read()) + return 0 + elif filepath: + logger.info(f"{log_file_path}") + return 0 + else: + logger.error(f"Error: teuthology.log file not found in {os.path.join(latest_directory, selected_jobID)}") + return 1 + return 0 + except Exception as e: + logger.error(f"Error: {e}") +