import os import re from typing import Any import yaml from frostfs_testlib import reporter from frostfs_testlib.shell.interfaces import CommandOptions, Shell from frostfs_testlib.storage.configuration.interfaces import ServiceConfigurationYml def extend_dict(extend_me: dict, extend_by: dict): if isinstance(extend_by, dict): for k, v in extend_by.items(): if k in extend_me: extend_dict(extend_me.get(k), v) else: extend_me[k] = v else: extend_me += extend_by class ServiceConfiguration(ServiceConfigurationYml): def __init__(self, service_name: str, shell: Shell, config_dir: str, main_config_path: str) -> None: self.service_name = service_name self.shell = shell self.main_config_path = main_config_path self.confd_path = os.path.join(config_dir, "conf.d") self.custom_file = os.path.join(self.confd_path, "99_changes.yml") def _path_exists(self, path: str) -> bool: return not self.shell.exec(f"test -e {path}", options=CommandOptions(check=False)).return_code def _get_config_files(self): config_files = [self.main_config_path] if self._path_exists(self.confd_path): files = self.shell.exec(f"find {self.confd_path} -type f").stdout.strip().split() # Sorting files in backwards order from latest to first one config_files.extend(sorted(files, key=lambda x: -int(re.findall("^\d+", os.path.basename(x))[0]))) return config_files def _get_configuration(self, config_files: list[str]) -> dict: if not config_files: return [{}] splitter = "+++++" files_str = " ".join(config_files) all_content = self.shell.exec( f"echo Getting config files; for file in {files_str}; do (echo {splitter}; sudo cat ${{file}}); done" ).stdout files_content = all_content.split("+++++")[1:] files_data = [yaml.safe_load(file_content) for file_content in files_content] mergedData = {} for data in files_data: extend_dict(mergedData, data) return mergedData def get(self, key: str) -> str | Any: with reporter.step(f"Get {key} configuration value for {self.service_name}"): config_files = self._get_config_files() configuration = self._get_configuration(config_files) result = self._find_option(key, configuration) return result def set(self, values: dict[str, Any]): with reporter.step(f"Change configuration for {self.service_name}"): if not self._path_exists(self.confd_path): self.shell.exec(f"mkdir {self.confd_path}") if self._path_exists(self.custom_file): data = self._get_configuration([self.custom_file]) else: data = {} for key, value in values.items(): self._set_option(key, value, data) content = yaml.dump(data) self.shell.exec(f"echo '{content}' | sudo tee {self.custom_file}") self.shell.exec(f"chmod 777 {self.custom_file}") def revert(self): with reporter.step(f"Revert changed options for {self.service_name}"): self.shell.exec(f"rm -rf {self.custom_file}")