diff --git a/README.md b/README.md index 1bbcae52..c7ae43a8 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It is currently under active development and has not yet had a formal release. ☑︎ CentOS 8.Stream mostly works - but has not yet passed a Ceph test -☐ A recent Fedora should work but has not been tested +☑︎ Works for Fedora 41 ☒ Ubuntu does not currently ship a new enough podman diff --git a/ceph_devstack/__init__.py b/ceph_devstack/__init__.py index c2c88d66..b684be6a 100644 --- a/ceph_devstack/__init__.py +++ b/ceph_devstack/__init__.py @@ -105,6 +105,33 @@ def parse_args(args: List[str]) -> argparse.Namespace: help="The container to wait for", ) subparsers.add_parser("show-conf", help="show the configuration") + parser_log= subparsers.add_parser( + "teuthology-log", help="See the teuthology test log files" + ) + parser_log.add_argument( + "--latest", + default=True, + action='store_true', + help="(Default) Get the latest teuthology test log", + ) + parser_log.add_argument( + "--dir", + default=False, + help="(Optional) Specify a custom logs archive directory instead of ~/.local/share/ceph-devstack \n FORMAT /your/custom/dir/ceph-devstack/archive", + ) + parser_log.add_argument( + "--path-only", + "-p", + default=False, + action='store_true', + help="Print the full path of teuthology.log instead of displaying its contents", + ) + parser_log.add_argument( + "--job", + "-j", + default=False, + help="(Optional) Specify a job ID to display logs for a particular job", + ) return parser.parse_args(args) diff --git a/ceph_devstack/cli.py b/ceph_devstack/cli.py index b3116f94..5895fbea 100644 --- a/ceph_devstack/cli.py +++ b/ceph_devstack/cli.py @@ -36,6 +36,8 @@ async def run(): return elif args.command == "wait": return await obj.wait(container_name=args.container) + elif args.command == "teuthology-log": + return await obj.teuthology_log(args) 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 95a8754e..0d480336 100644 --- a/ceph_devstack/resources/ceph/__init__.py +++ b/ceph_devstack/resources/ceph/__init__.py @@ -24,6 +24,7 @@ LoopControlDeviceWriteable, SELinuxModule, ) +from ceph_devstack.resources.log import LogCapabilities class SSHKeyPair(Secret): @@ -229,3 +230,13 @@ async def wait(self, container_name: str): return await container.wait() logger.error(f"Could not find container {container_name}") return 1 + + async def teuthology_log(self,args): + + if args.path_only: + LogCapabilities(path=args.dir, job_id=args.job).getPath() + else: + LogCapabilities(job_id=args.job).print_logs() + return 1 + + diff --git a/ceph_devstack/resources/log.py b/ceph_devstack/resources/log.py new file mode 100644 index 00000000..5bb33890 --- /dev/null +++ b/ceph_devstack/resources/log.py @@ -0,0 +1,75 @@ +import os +from datetime import datetime +from ceph_devstack import logger + +class LogCapabilities: + def __init__(self, path=None,job_id=None): + self.job_id = job_id or None + self.path = path or "~/.local/share/ceph-devstack/archive" + + def find_logs_location(self,): + user_path = os.path.expanduser(self.path) + if os.path.exists(user_path): + dirs = [d for d in os.listdir(user_path) if os.path.isdir(os.path.join(user_path, d))] + + if dirs: + dirs.sort(key=self._extract_timestamp, reverse=True) + latest_dir = dirs[0] + teuthology_logfiles = [] + for root, dirs, files in os.walk(os.path.join(user_path, latest_dir)): + if 'teuthology.log' in files: + if self.job_id is not None: + if os.path.basename(root) == str(self.job_id): + teuthology_logfilePath = os.path.join(root, 'teuthology.log') + teuthology_logfiles.append(teuthology_logfilePath) + else: + continue + else: + teuthology_logfilePath = os.path.join(root, 'teuthology.log') + teuthology_logfiles.append(teuthology_logfilePath) + return teuthology_logfiles + return [] + else: + return [] + + def getPath(self): + teuthology_logfile = self.find_logs_location() + if teuthology_logfile: + for idx, log in enumerate(teuthology_logfile): + logger.info(f"{idx + 1}: {log}") + else: + logger.info("teuthology.log not found in default dir. Try with --log-dir") + + def print_logs(self): + teuthology_logfile = self.find_logs_location() + if teuthology_logfile: + for idx, log in enumerate(teuthology_logfile): + logger.info(f"{idx + 1}: {log}") + + if len(teuthology_logfile) > 1: + selected_index = int(input("Multiple teuthology.log files found. Please select the log file by number: ")) - 1 + if 0 <= selected_index < len(teuthology_logfile): + selected_log = teuthology_logfile[selected_index] + else: + logger.info("Invalid selection.") + return [] + else: + selected_log = teuthology_logfile[0] + + with open(selected_log, 'r') as file: + log_contents = file.read() + logger.info(log_contents) + else: + logger.info("teuthology.log not found.") + + + + def _extract_timestamp(self, directory_name): + try: + timestamp_str = directory_name.split('-')[1] + '-' + directory_name.split('-')[2] + '-' + directory_name.split('-')[3] + '_' + directory_name.split('-')[4] + timestamp_str = timestamp_str.split('_')[0] + '_' + timestamp_str.split('_')[1].split('-')[0] + return datetime.strptime(timestamp_str, '%Y-%m-%d_%H:%M:%S') + except Exception as e: + return datetime.min + + \ No newline at end of file