From c44a9e481d7025e18535c87e1745e306c2f679ae Mon Sep 17 00:00:00 2001 From: dingjiahuichina Date: Sun, 20 Apr 2025 23:47:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20oedp=E6=94=AF=E6=8C=81=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/Command.md | 176 ++++++++++-- oedp/build/oedp.spec | 14 +- oedp/src/commands/repo/repo_cmd.py | 367 +++++++++++++++++++++++++ oedp/src/config/repo/repo.conf | 7 + oedp/src/constants/paths.py | 10 +- oedp/src/parsers/oedp_parser.py | 92 +++++++ oedp/src/utils/log/logger_generator.py | 9 - 7 files changed, 632 insertions(+), 43 deletions(-) create mode 100644 oedp/src/commands/repo/repo_cmd.py create mode 100644 oedp/src/config/repo/repo.conf diff --git a/doc/Command.md b/doc/Command.md index 8085229..46e4c8b 100644 --- a/doc/Command.md +++ b/doc/Command.md @@ -1,4 +1,4 @@ -# oedp 命令行参数说明 +# # `oedp`参数说明 ## `oedp` @@ -10,48 +10,168 @@ 查看项目的详细信息,默认为当前路径 -| 选项 | 简写 | 是否必需 | 功能说明 | -| ------------------ | ---- | -------- | ---------------------- | -| `--project [path]` | `-p` | N | 指定一个路径为项目路径 | +| 选项 | 简写 | 是否必需 | 功能说明 | +| ------------------ | ---- | -------- | ------------------------ | +| `--project [path]` | `-p` | N | 项目路径,默认为当前路径 | ## `oedp run [action]` -执行项目的方法,默认为当前路径;`[action]`为插件可使用的方法,可通过 `oedp info`命令查询 +执行项目某个方法,默认为当前路径;`[action]`为插件可使用的方法,可通过 `oedp info`命令查询 -| 选项 | 简写 | 是否必需 | 功能说明 | -| ------------------- | ---- | -------- | ---------------------- | -| `--project [path]` | `-p` | N | 指定一个路径为项目路径 | -| `--debug`(开发中) | `-d` | N | 以debug模式运行 | +| 选项 | 简写 | 是否必需 | 功能说明 | +| ------------------ | ---- | -------- | ------------------------ | +| `--project [path]` | `-p` | N | 项目路径,默认为当前路径 | +| `--debug` | `-d` | N | 以debug模式运行 | -## `oedp list` +## `oedp list`(开发中) 列举源中可用的插件 -| 选项 | 简写 | 是否必需 | 功能说明 | -| ---------------- | ---- | -------- | -------------------- | -| `--local [path]` | `-l` | N | 指定一个路径为本地源 | +## `oedp init [plugin]`(开发中) + +插件初始化到指定路径,`[plugin]`可以是插件压缩包路径、插件下载地址、插件名称 + +- `[plugin]`如果为本地的插件压缩包,则直接初始化到指定路径 +- `[plugin]`如果为插件下载地址,则先下载到缓存路径,再初始化到指定路径 +- `[plugin]`如果为插件名称,优先在本地源查找,后在远端源查找,下载后初始化到指定路径(开发中) + +| 选项 | 简写 | 是否必需 | 功能说明 | +| ------------------ | ---- | -------- | ------------------------------------------------------------ | +| `--project [path]` | `-p` | Y | 项目路径,若不存在则创建 | +| `--force` | `-f` | N | 强制覆盖路径,请谨慎使用;如果路径存在,会先删除该路径中的所有文件,再初始化 | + +## `oedp repo`(开发中) + +插件源管理。支持本地插件源、远端插件源。 + +| 选项 | 功能说明 | +| ------------------ | ---------------------- | +| `list` | 查询所有已配置的插件源 | +| `update` | 更新插件索引缓存 | +| `set [name] [url]` | 修改插件源地址 | +| `del [name]` | 删除插件源 | +| `enable [name]` | 使能插件源 | +| `disable [name]` | 去使能插件源 | + +插件源配置文件`/etc/oedp/config/repo/repo.conf`示例 + +````ini +[openEuler] +url = https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/contrib/oedp/plugins/ +enabled = true + +[local] +url = file:///root/.oedp/plugins/ +enabled = false +```` ## `oedp check [action]`(开发中) 检查项目中指定方法的检查项,默认为当前路径 -| 选项 | 简写 | 是否必需 | 功能说明 | -| ------------------ | ---- | -------- | ---------------------- | -| `--project [path]` | `-p` | N | 指定一个路径为项目路径 | +| 选项 | 简写 | 是否必需 | 功能说明 | +| ------------------ | ---- | -------- | ------------------------ | +| `--project [path]` | `-p` | N | 项目路径,默认为当前路径 | + +## `oedp clean`(开发中) -## `oedp init [plugin]`(开发中) +清理所有临时插件文件。 -插件初始化, `[plugin]`为插件名,可通过 `oedp list`命令查询 +# # `oedp`工具相关文件与路径 -| 选项 | 简写 | 是否必需 | 功能说明 | -| ------------------ | ---- | -------- | ------------------------------------------------------------ | -| `--project [path]` | `-p` | Y | 指定初始化路径 | -| `--local [path]` | `-l` | N | 指定一个路径为本地源 | -| `--force` | `-f` | N | 强制覆盖路径;如果路径存在,会先删除该路径中的所有文件,再初始化 | +| 路径 | 说明 | +| ------------------------------------------- | ---------------------- | +| `/etc/oedp/config/` | 配置文件路径 | +| `/etc/oedp/config/repo/cache/`(开发中) | 插件源索引文件缓存路径 | +| `/etc/oedp/config/repo/repo.conf`(开发中) | 插件源配置文件 | +| `/usr/lib/oedp/src/` | 源码路径 | +| `/var/oedp/log/` | 日志文件路径 | +| `/var/oedp/plugin/` | 插件缓存路径 | + +# # 插件源(开发中) + +## 格式说明 + +oeDeploy 支持从可访问的插件源自动获取安装部署插件,并初始化到本地。 + +插件源的根目录下必须有一个索引文件`index.yaml`,其描述了当前插件源中所有可访问插件的具体路径。 + +oeDeploy 提供了脚本`tools/build_repo/make_repo_index.py`,用于自动生成`index.yaml`。 + +`index.yaml`格式如下: + +```yaml +--- +apiversion: v1 +plugins: +- kubernetes-1.31.1: + - name: kubernetes-1.31.1 + version: 1.0.0-1 + updated: "2025-03-05T10:31:02.608017752+08:00" + description: oeDeploy pulgin for kubernetes deployment + icon: https://gitee.com/openeuler/oeDeploy/blob/master/oedp/build/static/oeDeploy.png + type: app + sha256sum: 683995d14bbf425f18d1086858f210fc704b1d14edb274382d0e518a5d2a92c1 + size: 1061949301 + urls: + - https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/contrib/oedp/plugins/kubernetes-1.31.1.tar.gz + - name: kubernetes-1.31.1 + version: 1.0.0-2 + ... +- pytorch: + - name: pytorch + version: 1.0.0-1 + updated: "2025-03-05T10:31:02.608017752+08:00" + description: oeDeploy pulgin for pytorch deployment + icon: https://gitee.com/openeuler/oeDeploy/blob/master/oedp/build/static/oeDeploy.png + type: app + sha256sum: 3fe0cb97e01ac9af2c1c8d12d12753f82988ab0e39b5878f359829574102c79d + size: 2373 + urls: + - https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/contrib/oedp/plugins/pytorch.tar.gz + ... +... +``` + +插件字段含义: + +| 字段 | 含义 | 获取方式 | +| ------------- | ------------------------------------------------------------ | ------------- | +| `name` | 插件名称。允许插件名称中带有版本号,表示软件本身的版本,而非插件的版本。 | 读取main.yaml | +| `version` | 插件版本号。 | 读取main.yaml | +| `updated` | 插件更新的时间。允许为空。 | 读取main.yaml | +| `description` | 插件介绍。允许为空。 | 读取main.yaml | +| `icon` | 插件图标地址,用于Web端显示。允许为空。 | 读取main.yaml | +| `type` | 插件类型。保留字段,暂不生效。默认值 `app`。 | 读取main.yaml | +| `sha256sum` | 插件文件sha256数值,用于下载时的完整性校验。 | 脚本生成 | +| `size` | 插件文件大小,单位Bytes。 | 脚本生成 | +| `urls` | 插件下载地址,可以有多个。所有url地址都必须在当前插件源目录的范围内。 | 脚本生成 | + +其他字段: + +| 字段 | 含义 | 获取方式 | +| ------------ | ------------------------------------------------------- | -------- | +| `apiversion` | 索引文件格式的版本号。保留字段,暂不生效。默认值 `v1`。 | 脚本生成 | + +## 插件源构建方式(开发中) + +执行如下命令,一键生成索引文件`index.yaml`: + +````bash +python3 make_repo_index.py [plugins_dir] [url_prefix] [output_dir] +```` + +`plugins_dir`表示当前 Linux 环境下的插件源根目录,脚本会自动识别目录下所有符合条件的 oeDeploy 插件 + +`url_prefix`表示每个插件源的 url 索引前缀,即对外暴露的插件源根目录 + +`output_dir`表示索引文件`index.yaml`输出的目录 + +例如,当前 Linux 环境上`/root/build_workspace/storages/plugins`目录中有多个 oeDeploy 插件,同时映射到`http://x.x.x.x:8080/plugins`,作为文件服务器对外暴露。 -# oedp 工具相关路径 +执行如下命令,可以在`/root/build_workspace/storages/plugins`目录下生成一个`index.yaml`,其中每个插件的`urls`字段是其在文件服务器上的访问地址。 + +````bash +python3 make_repo_index.py /root/build_workspace/storages/plugins http://x.x.x.x:8080/plugins /root/build_workspace/storages/plugins +```` -+ `/var/oedp/log/`:日志文件路径 -+ `/var/oedp/plugin/`:插件缓存路径 -+ `/usr/lib/oedp/src/`:源码路径 -+ `/etc/oedp/config/`:配置文件路径 diff --git a/oedp/build/oedp.spec b/oedp/build/oedp.spec index 346d904..a03bd46 100644 --- a/oedp/build/oedp.spec +++ b/oedp/build/oedp.spec @@ -32,8 +32,11 @@ mkdir -p -m 700 %{buildroot}%{_usr}/share/applications mkdir -p -m 700 %{buildroot}%{_sysconfdir}/oedp/config mkdir -p %{buildroot}%{_bindir} +touch %{buildroot}%{_var}/oedp/log/oedp.log cp -rdpf %{_builddir}/%{name}-%{version}/src %{buildroot}%{_usr}/lib/oedp -mv %{buildroot}%{_usr}/lib/oedp/src/config/log.conf %{buildroot}%{_sysconfdir}/oedp/config +rm -rf %{buildroot}%{_sysconfdir}/oedp/config/* +mv %{buildroot}%{_usr}/lib/oedp/src/config/* %{buildroot}%{_sysconfdir}/oedp/config +mkdir -p -m 700 %{buildroot}%{_sysconfdir}/oedp/config/repo/cache install -c -m 0400 %{_builddir}/%{name}-%{version}/static/* %{buildroot}%{_usr}/share/applications install -c -m 0500 %{_builddir}/%{name}-%{version}/oedp.py %{buildroot}%{_bindir}/oedp @@ -41,6 +44,7 @@ install -c -m 0500 %{_builddir}/%{name}-%{version}/oedp.py %{buildroot}%{_bindir %postun if [ $1 -eq 0 ]; then # 卸载时删除可能会残留的目录 + rm -rf /etc/oedp rm -rf /var/oedp rm -rf /usr/lib/oedp fi @@ -49,15 +53,18 @@ fi %files %attr(0555,root,root) %dir %{_var}/oedp %attr(0777,root,root) %dir %{_var}/oedp/log -%attr(0555,root,root) %dir %{_var}/oedp/plugin +%attr(0777,root,root) %dir %{_var}/oedp/plugin %attr(0555,root,root) %dir %{_var}/oedp/python %attr(0555,root,root) %dir %{_var}/oedp/python/venv %attr(0555,root,root) %dir %{_usr}/lib/oedp %attr(0777,root,root) %dir %{_sysconfdir}/oedp/config +%attr(0777,root,root) %dir %{_sysconfdir}/oedp/config/repo +%attr(0777,root,root) %dir %{_sysconfdir}/oedp/config/repo/cache -%attr(0666,root,root) %ghost %{_var}/oedp/log/oedp.log +%attr(0666,root,root) %{_var}/oedp/log/oedp.log %attr(0555,root,root) %{_usr}/lib/oedp/src/* %attr(0666,root,root) %config(noreplace) %{_sysconfdir}/oedp/config/log.conf +%attr(0666,root,root) %config(noreplace) %{_sysconfdir}/oedp/config/repo/repo.conf %attr(0644,root,root) %{_usr}/share/applications/* %attr(0555,root,root) %{_bindir}/oedp @@ -79,4 +86,3 @@ fi - fix the issue of abnormal termination during the script execution phase after installation * Sat Feb 22 2025 Liu Jiangbin - 1.0.0-1 -- init package \ No newline at end of file diff --git a/oedp/src/commands/repo/repo_cmd.py b/oedp/src/commands/repo/repo_cmd.py new file mode 100644 index 0000000..fa60a94 --- /dev/null +++ b/oedp/src/commands/repo/repo_cmd.py @@ -0,0 +1,367 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2024 Huawei Technologies Co., Ltd. +# oeDeploy is licensed under the Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# http://license.coscl.org.cn/MulanPSL2 +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# PURPOSE. +# See the Mulan PSL v2 for more details. +# Create: 2025-04-19 +# ====================================================================================================================== + +import os +import configparser +from prettytable import PrettyTable +from typing import Set +from src.utils.command.command_executor import CommandExecutor +from src.utils.log.logger_generator import LoggerGenerator +from src.constants.paths import REPO_CONFIG_PATH, REPO_CACHE_DIR + +class RepoCmd: + """插件源管理命令处理器""" + + def __init__(self, args): + """初始化RepoCmd + + Args: + args: 命令行参数对象 + """ + self.args = args + self.log = LoggerGenerator().get_logger('repo_cmd') + + def run(self) -> bool: + """执行repo子命令 + + Returns: + bool: 命令执行结果,成功返回True,失败返回False + """ + command_map = { + 'list': self.run_list, + 'update': self.run_update, + 'set': self.run_set, + 'del': self.run_del, + 'enable': self.run_enable, + 'disable': self.run_disable + } + handler = command_map.get(self.args.subcommand) + return handler() if handler else False + + def run_list(self) -> bool: + """列出所有已配置的插件源 + + Returns: + bool: 执行结果 + """ + + if not self._check_config_and_cache_dir(): + return False + + config = self._read_repo_config() + if not config: + return False + + table = PrettyTable() + table.field_names = ["name", "url", "enabled", "available"] + table.align["name"] = "l" # 左对齐 + table.align["url"] = "l" # 左对齐 + + for section in config.sections(): + name = section + url = config.get(section, 'url') if config.has_option(section, 'url') else '' + enabled = config.get(section, 'enabled') if config.has_option(section, 'enabled') else 'false' + + # 检查缓存文件是否存在 + cache_file = os.path.join(REPO_CACHE_DIR, f"{section}.yaml") + available = 'true' if os.path.exists(cache_file) else 'false' + + table.add_row([name, url, enabled, available]) + + self.log.info("\n" + str(table)) + return True + + def run_update(self) -> bool: + """更新插件索引缓存 + + 1. 读取repo.conf配置文件 + 2. 对每个启用的repo下载index.yaml到缓存目录 + 3. 清理不需要的缓存文件 + + Returns: + bool: 只要成功更新一个repo索引就返回True,否则返回False + """ + if not self._check_config_and_cache_dir(): + return False + + config = self._read_repo_config() + if not config: + return False + + downloaded_files = self._process_repositories(config) + self._cleanup_old_cache_files(downloaded_files) + + return len(downloaded_files) > 0 + + def run_set(self) -> bool: + """新增/修改插件源地址 + + Returns: + bool: 执行结果 + """ + name = self.args.name + url = self.args.url + + config = self._read_repo_config() + if not config: + return False + + try: + if not config.has_section(name): + config.add_section(name) + config.set(name, 'enabled', 'true') + + config.set(name, 'url', url) + + with open(REPO_CONFIG_PATH, 'w') as configfile: + config.write(configfile) + + self.log.info(f"{'added' if not config.has_section(name) else 'updated'} repo: {name}") + return self._download_repo_index(config, name) + except Exception as e: + self.log.error(f"failed to set repo {name}: {str(e)}") + return False + + def run_del(self) -> bool: + """删除指定插件源 + + Returns: + bool: 执行结果 + """ + name = self.args.name + + config = self._read_repo_config() + if not config: + return False + + if not config.has_section(name): + self.log.error(f"Repo {name} not found") + return False + + try: + config.remove_section(name) + with open(REPO_CONFIG_PATH, 'w') as configfile: + config.write(configfile) + + self.log.info(f"removed repo: {name}") + return self._remove_repo_cache(name) + except Exception as e: + self.log.error(f"failed to remove repo {name}: {str(e)}") + return False + + def run_enable(self) -> bool: + """启用插件源 + + Returns: + bool: 执行结果 + """ + name = self.args.name + + config = self._read_repo_config() + if not config: + return False + + if not config.has_section(name): + self.log.error(f"Repo {name} not found") + return False + + try: + config.set(name, 'enabled', 'true') + with open(REPO_CONFIG_PATH, 'w') as configfile: + config.write(configfile) + + self.log.info(f"enabled repo: {name}") + return self._download_repo_index(config, name) + except Exception as e: + self.log.error(f"failed to enable repo {name}: {str(e)}") + return False + + def run_disable(self) -> bool: + """禁用插件源 + + Returns: + bool: 执行结果 + """ + name = self.args.name + + config = self._read_repo_config() + if not config: + return False + + if not config.has_section(name): + self.log.error(f"Repo {name} not found") + return False + + try: + config.set(name, 'enabled', 'false') + with open(REPO_CONFIG_PATH, 'w') as configfile: + config.write(configfile) + + self.log.info(f"disabled repo: {name}") + return self._remove_repo_cache(name) + except Exception as e: + self.log.error(f"failed to disable repo {name}: {str(e)}") + return False + + def _check_config_and_cache_dir(self) -> bool: + """检查配置文件和缓存目录是否存在 + + Returns: + bool: 检查是否通过 + """ + if not os.path.exists(REPO_CONFIG_PATH): + self.log.error(f"repo config file not found: {REPO_CONFIG_PATH}") + return False + + try: + os.makedirs(REPO_CACHE_DIR, mode=0o755, exist_ok=True) + return True + except Exception as e: + self.log.error(f"failed to create cache directory: {str(e)}") + return False + + def _read_repo_config(self) -> configparser.ConfigParser: + """读取repo配置文件 + + Returns: + ConfigParser: 解析后的配置对象,失败返回None + """ + config = configparser.ConfigParser() + try: + config.read(REPO_CONFIG_PATH) + return config + except Exception as e: + self.log.error(f"failed to read repo config: {str(e)}") + return None + + def _process_repositories(self, config: configparser.ConfigParser) -> Set[str]: + """处理所有启用的仓库,下载索引文件 + + Args: + config: 解析后的配置对象 + + Returns: + Set[str]: 成功下载的文件路径集合 + """ + downloaded_files = set() + + for section in config.sections(): + # 检查仓库是否启用 + if not (config.has_option(section, 'enabled') and + config.has_option(section, 'url') and + config.getboolean(section, 'enabled')): + continue + + url = config.get(section, 'url').rstrip('/') + '/index.yaml' + output_file = os.path.join(REPO_CACHE_DIR, f"{section}.yaml") + + success = self._download_with_retry(url, output_file, section) + if success: + downloaded_files.add(output_file) + + return downloaded_files + + def _cleanup_old_cache_files(self, downloaded_files: Set[str]): + """清理旧的缓存文件 + + Args: + downloaded_files: 当前下载的文件路径集合 + """ + try: + for filename in os.listdir(REPO_CACHE_DIR): + if not filename.endswith('.yaml'): + continue + + filepath = os.path.join(REPO_CACHE_DIR, filename) + if filepath not in downloaded_files: + try: + os.remove(filepath) + except Exception as e: + self.log.error(f"failed to remove old cache file {filename}: {str(e)}") + except Exception as e: + self.log.error(f"failed to clean up cache directory: {str(e)}") + + def _download_repo_index(self, config: configparser.ConfigParser, name: str) -> bool: + """下载指定repo的index.yaml文件 + + Args: + config: 配置对象 + name: repo名称 + + Returns: + bool: 下载是否成功 + """ + if not self._check_config_and_cache_dir(): + return False + + url = config.get(name, 'url').rstrip('/') + '/index.yaml' + output_file = os.path.join(REPO_CACHE_DIR, f"{name}.yaml") + + return self._download_with_retry(url, output_file, name) + + def _remove_repo_cache(self, name: str) -> bool: + """删除指定repo的缓存文件 + + Args: + name: repo名称 + + Returns: + bool: 删除是否成功 + """ + cache_file = os.path.join(REPO_CACHE_DIR, f"{name}.yaml") + if os.path.exists(cache_file): + try: + os.remove(cache_file) + self.log.info(f"removed cache file for repo: {name}") + return True + except Exception as e: + self.log.error(f"failed to remove cache file {cache_file}: {str(e)}") + return False + return True + + def _download_with_retry(self, url: str, output_file: str, repo_name: str, max_retries: int = 3) -> bool: + """带重试机制的下载方法 + + Args: + url: 下载URL + output_file: 输出文件路径 + repo_name: 仓库名称 + max_retries: 最大重试次数 + + Returns: + bool: 下载是否成功 + """ + cmd = [ + 'curl', '-fL', + '--max-time', '10', + '--connect-timeout', '3', + '-o', output_file, + url + ] + + for attempt in range(max_retries): + _, stderr, return_code = CommandExecutor.run_single_cmd(cmd) + + if return_code == 0: + try: + self.log.info(f"updated index for repo: {repo_name}") + return True + except Exception as e: + self.log.error(f"failed to set permissions for {output_file}: {str(e)}") + return False + + if attempt < max_retries - 1: + self.log.warning(f"retrying download for repo {repo_name} (attempt {attempt + 1}/{max_retries})") + + self.log.error(f"failed to download index for repo {repo_name} after {max_retries} attempts:\n{stderr}") + return False diff --git a/oedp/src/config/repo/repo.conf b/oedp/src/config/repo/repo.conf new file mode 100644 index 0000000..68a564c --- /dev/null +++ b/oedp/src/config/repo/repo.conf @@ -0,0 +1,7 @@ +[openEuler] +url = https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/contrib/oedp/plugins/ +enabled = true + +[local] +url = file:///root/.oedp/plugins/ +enabled = false diff --git a/oedp/src/constants/paths.py b/oedp/src/constants/paths.py index a527af1..ab1b688 100644 --- a/oedp/src/constants/paths.py +++ b/oedp/src/constants/paths.py @@ -46,7 +46,13 @@ LOG_DIR = join(OEDP_HOME, "log") # oedp 配置家目录 OEDP_CONFIG_HOME_DIR = "/etc/oedp" -# 配置文件目录 +# /etc/oedp/config 配置文件目录 OEDP_CONFIG_DIR = join(OEDP_CONFIG_HOME_DIR, "config") -# 日志配置文件所在目录 +# /etc/oedp/config/log 日志配置文件所在目录 LOG_CONFIG_DIR = join(OEDP_CONFIG_DIR, "log") +# /etc/oedp/config/repo 插件源相关配置所在目录 +REPO_CONFIG_DIR = join(OEDP_CONFIG_DIR, "repo") +# /etc/oedp/config/repo/repo.conf 插件源配置文件 +REPO_CONFIG_PATH = join(REPO_CONFIG_DIR, "repo.conf") +# /etc/oedp/config/repo/cache 插件源索引缓存目录 +REPO_CACHE_DIR = join(REPO_CONFIG_DIR, "cache") diff --git a/oedp/src/parsers/oedp_parser.py b/oedp/src/parsers/oedp_parser.py index 3e40a7c..8f455a6 100644 --- a/oedp/src/parsers/oedp_parser.py +++ b/oedp/src/parsers/oedp_parser.py @@ -20,6 +20,7 @@ from src.commands.info.info_cmd import InfoCmd from src.commands.init.init_cmd import InitCmd from src.commands.list.list_cmd import ListCmd from src.commands.run.run_cmd import RunCmd +from src.commands.repo.repo_cmd import RepoCmd from src.constants.const import VERSION from src.constants.paths import PLUGIN_DIR @@ -50,6 +51,7 @@ class OeDeployParser: self._add_info_command() self._add_run_command() self._add_check_command() + self._add_repo_command() def execute(self): """ @@ -202,6 +204,92 @@ class OeDeployParser: ) check_command.set_defaults(func=self._run_check_command) + def _add_repo_command(self): + """ + oedp repo [] + + 插件源管理 + """ + repo_command = self.subparsers.add_parser( + 'repo', + prog='oedp repo', + help='Manage plugin repositories', + usage='%(prog)s []' + ) + subparsers = repo_command.add_subparsers( + dest='subcommand', + title='Available subcommands', + required=True, + metavar='' + ) + + # repo list + list_parser = subparsers.add_parser( + 'list', + help='List all configured repositories' + ) + list_parser.set_defaults(func=self._run_repo_command) + + # repo update + update_parser = subparsers.add_parser( + 'update', + help='Update repository index cache' + ) + update_parser.set_defaults(func=self._run_repo_command) + + # repo set + set_parser = subparsers.add_parser( + 'set', + help='Modify repository URL' + ) + set_parser.add_argument( + 'name', + type=str, + help='Repository name' + ) + set_parser.add_argument( + 'url', + type=str, + help='New repository URL' + ) + set_parser.set_defaults(func=self._run_repo_command) + + # repo del + del_parser = subparsers.add_parser( + 'del', + help='Delete a repository' + ) + del_parser.add_argument( + 'name', + type=str, + help='Repository name to delete' + ) + del_parser.set_defaults(func=self._run_repo_command) + + # repo enable + enable_parser = subparsers.add_parser( + 'enable', + help='Enable a repository' + ) + enable_parser.add_argument( + 'name', + type=str, + help='Repository name to enable' + ) + enable_parser.set_defaults(func=self._run_repo_command) + + # repo disable + disable_parser = subparsers.add_parser( + 'disable', + help='Disable a repository' + ) + disable_parser.add_argument( + 'name', + type=str, + help='Repository name to disable' + ) + disable_parser.set_defaults(func=self._run_repo_command) + @staticmethod def _run_init_command(args): plugin = args.plugin @@ -233,6 +321,10 @@ class OeDeployParser: group = args.group return CheckCmd(project, group).run() + @staticmethod + def _run_repo_command(args): + return RepoCmd(args).run() + @staticmethod def _get_version(): stdout, _, return_code = CommandExecutor.run_single_cmd(['rpm', '-q', 'oedp']) diff --git a/oedp/src/utils/log/logger_generator.py b/oedp/src/utils/log/logger_generator.py index ea72fb8..3994827 100644 --- a/oedp/src/utils/log/logger_generator.py +++ b/oedp/src/utils/log/logger_generator.py @@ -55,7 +55,6 @@ class LoggerGenerator: def _add_file_handler(self, logger): log_dir = os.path.dirname(LOG_CONFIG_OBJ.log_file_path) os.makedirs(log_dir, mode=DIR_MODE, exist_ok=True) - self._change_file_mode_for_writing() file_handler = logging.handlers.RotatingFileHandler( filename=LOG_CONFIG_OBJ.log_file_path, maxBytes=LOG_CONFIG_OBJ.file_max_size, @@ -65,11 +64,3 @@ class LoggerGenerator: file_formatter = logging.Formatter(LOG_CONFIG_OBJ.file_log_format) file_handler.setFormatter(file_formatter) logger.addHandler(file_handler) - - def _change_file_mode_for_writing(self): - if LOG_CONFIG_OBJ.log_file_path: - if not os.path.exists(LOG_CONFIG_OBJ.log_file_path): - os.fdopen( - os.open(LOG_CONFIG_OBJ.log_file_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, LOG_MODE_WRITING), - 'w').close() - os.chmod(LOG_CONFIG_OBJ.log_file_path, LOG_MODE_WRITING) -- Gitee