diff --git a/esd/spack_manager/SpackManager.py b/esd/spack_manager/SpackManager.py index cdf4a1d36d249017636798b05349a88b5e67f97e..f7bb00b4e7ba348ba840adac81065005f65eaab7 100644 --- a/esd/spack_manager/SpackManager.py +++ b/esd/spack_manager/SpackManager.py @@ -1,5 +1,4 @@ import os -import subprocess from abc import ABC, abstractmethod from pathlib import Path @@ -26,11 +25,17 @@ class SpackManager(ABC): def __init__(self, env: SpackModel = None, repos=None, upstream_instance=None, system_name: str = None, logger=get_logger(__name__)): if repos is None: - self.repos = list() + repos = [] + self.repos = repos self.env = env - self.upstream_instance = upstream_instance - self.install_dir = Path(os.environ.get("INSTALLATION_ROOT") or os.getcwd()) + self.install_dir = Path(os.environ.get("INSTALLATION_ROOT") or os.getcwd()).resolve() self.install_dir.mkdir(parents=True, exist_ok=True) + self.env_path = None + if self.env and self.env.path: + self.env.path = self.env.path.resolve() + self.env.path.mkdir(parents=True, exist_ok=True) + self.env_path = self.env.path / self.env.env_name + self.upstream_instance = upstream_instance self.spack_dir = self.install_dir / "spack" self.spack_setup_script = self.spack_dir / "share" / "spack" / "setup-env.sh" self.logger = logger @@ -45,23 +50,17 @@ class SpackManager(ABC): pass def create_fetch_spack_environment(self): - env_dir = self.install_dir / self.env.path / self.env.env_name + if self.env.git_path: - try: - git_clone_repo(self.env.env_name, env_dir, self.env.git_path, logger=self.logger) - except subprocess.CalledProcessError as e: - self.logger.exception(f'Failed to clone repository: {self.env.env_name}: {e}') - raise BashCommandException(f'Failed to clone repository: {self.env.env_name}: {e}') + git_clone_repo(self.env.env_name, self.env.path / self.env.env_name, self.env.git_path, logger=self.logger) else: - try: - os.makedirs(self.env.path / self.env.env_name, exist_ok=True) - run_command("bash", "-c", - f'source {self.spack_setup_script} && spack env create -d {self.env.path}/{self.env.env_name}', - check=True, logger=self.logger) - self.logger.debug(f"Created {self.env.env_name} spack environment") - except subprocess.CalledProcessError as e: - self.logger.error(f"Failed to create {self.env.env_name} spack environment") - raise BashCommandException(f"Failed to create {self.env.env_name} spack environment") + os.makedirs(self.env.path / self.env.env_name, exist_ok=True) + run_command("bash", "-c", + f'source {self.spack_setup_script} && spack env create -d {self.env_path}', + check=True, logger=self.logger, + debug_msg=f"Created {self.env.env_name} spack environment", + exception_msg=f"Failed to create {self.env.env_name} spack environment", + exception=BashCommandException) def setup_spack_env(self): """ @@ -94,46 +93,51 @@ class SpackManager(ABC): def spack_repo_exists(self, repo_name: str) -> bool: """Check if the given Spack repository exists.""" if self.env is None: - try: - result = run_command("bash", "-c", - f'source {self.spack_setup_script} && spack repo list', - check=True, - capture_output=True, text=True, logger=self.logger) - except subprocess.CalledProcessError: + result = run_command("bash", "-c", + f'source {self.spack_setup_script} && spack repo list', + check=True, + capture_output=True, text=True, logger=self.logger, + debug_msg=f'Checking if {repo_name} exists') + if result is None: return False else: - try: - result = run_command("bash", "-c", - f'source {self.spack_setup_script} && spack env activate -p {self.env.path}/{self.env.env_name} && spack repo list', - check=True, - capture_output=True, text=True, logger=self.logger) - except subprocess.CalledProcessError: + result = run_command("bash", "-c", + f'source {self.spack_setup_script} && spack env activate -p {self.env_path} && spack repo list', + check=True, + capture_output=True, text=True, logger=self.logger, + debug_msg=f'Checking if repository {repo_name} was added') + if result is None: return False return any(line.strip().endswith(repo_name) for line in result.stdout.splitlines()) + def spack_env_exists(self): + result = run_command("bash", "-c", + f'source {self.spack_setup_script} && spack env activate -p {self.env_path}', + check=True, + capture_output=True, text=True, logger=self.logger, + debug_msg=f'Checking if environment {self.env.env_name} exists') + if result is None: + return False + return True + def add_spack_repo(self, repo_path: Path, repo_name: str): """Add the Spack repository if it does not exist.""" - repo_path = repo_path.resolve().as_posix() - try: - run_command("bash", "-c", - f'source {self.spack_setup_script} && spack env activate -p {self.env.path}/{self.env.env_name} && spack repo add {repo_path}/{repo_name}', - check=True, logger=self.logger) - self.logger.debug(f"Added {repo_name} to spack environment {self.env.env_name}") - except subprocess.CalledProcessError as e: - self.logger.error(f"Failed to add {repo_name} to spack environment {self.env.env_name}") - raise BashCommandException(f"Failed to add {repo_name} to spack environment {self.env.env_name}: {e}") + run_command("bash", "-c", + f'source {self.spack_setup_script} && spack env activate -p {self.env_path} && spack repo add {repo_path}/{repo_name}', + check=True, logger=self.logger, + debug_msg=f"Added {repo_name} to spack environment {self.env.env_name}", + exception_msg=f"Failed to add {repo_name} to spack environment {self.env.env_name}", + exception=BashCommandException) def get_spack_installed_version(self): - try: - spack_version = run_command("bash", "-c", f'source {self.spack_setup_script} && spack --version', - capture_output=True, text=True, check=True, - logger=self.logger) - spack_version = spack_version.stdout.strip().split()[0] - self.logger.debug(f"Getting spack version: {spack_version}") - return spack_version - except subprocess.SubprocessError as e: - self.logger.error(f"Error retrieving Spack version: {e}") - return None + spack_version = run_command("bash", "-c", f'source {self.spack_setup_script} && spack --version', + capture_output=True, text=True, check=True, + logger=self.logger, + debug_msg=f"Getting spack version", + exception_msg=f"Error retrieving Spack version") + if spack_version: + return spack_version.stdout.strip().split()[0] + return None def install_spack(self, spack_version="v0.21.1", spack_repo='https://github.com/spack/spack'): try: @@ -163,8 +167,9 @@ class SpackManager(ABC): bashrc.write(f"source {self.spack_setup_script}\n") self.logger.info("Added Spack PATH to .bashrc") if user: - run_command("chown", "-R", f"{user}:{user}", self.spack_dir, check=True, logger=self.logger) - run_command("bash", "-c", f"source {bashrc_path}", check=True, logger=self.logger) + run_command("chown", "-R", f"{user}:{user}", self.spack_dir, check=True, logger=self.logger, + debug_msg='Adding permissions to the logged in user') + run_command("bash", "-c", f"source {bashrc_path}", check=True, logger=self.logger, debug_msg='Restart bash') self.logger.info("Spack install completed") # Restart Bash after the installation ends os.system("exec bash") diff --git a/esd/utils/utils.py b/esd/utils/utils.py index d229e3453cb3ba4b29319ec84eeeb68998851447..48c500c3a5f652dad4e5c9a3aec06120305f8cd9 100644 --- a/esd/utils/utils.py +++ b/esd/utils/utils.py @@ -3,6 +3,8 @@ import shutil import subprocess from pathlib import Path +from esd.error_handling.exceptions import BashCommandException + def clean_up(dirs: list[str], logging, ignore_errors=True): """ @@ -22,21 +24,30 @@ def clean_up(dirs: list[str], logging, ignore_errors=True): logging.info(f"{cleanup_dir} does not exist") -def run_command(*args, logger: None, **kwargs): - if logger is None: - logger = logging.getLogger(__name__) - logger.debug(f'{args}') - return subprocess.run(args, **kwargs) +def run_command(*args, logger=logging.getLogger(__name__), debug_msg: str = '', exception_msg: str = None, + exception=None, **kwargs): + try: + logger.debug(f'{debug_msg}: args: {args}') + return subprocess.run(args, **kwargs) + except subprocess.CalledProcessError as e: + if exception_msg is not None: + logger.error(f"{exception_msg}: {e}") + if exception is not None: + raise exception(f'{exception_msg} : {e}') + else: + return None -def git_clone_repo(repo_name: str, dir: Path, git_path: str, logger: logging): +def git_clone_repo(repo_name: str, dir: Path, git_path: str, logger: logging = logging.getLogger(__name__)): if not dir.exists(): run_command( "git", "clone", "--depth", "1", "-c", "advice.detachedHead=false", "-c", "feature.manyFiles=true", git_path, dir - , check=True, logger=logger) - logger.debug(f'Cloned repository {repo_name}') + , check=True, logger=logger, + debug_msg=f'Cloned repository {repo_name}', + exception_msg=f'Failed to clone repository: {repo_name}', + exception=BashCommandException) else: logger.debug(f'Repository {repo_name} already cloned.')