add: read args & parse config

This commit is contained in:
Sergei Erjemin 2025-06-05 23:53:17 +03:00
parent 327e0a55e9
commit 4a81ed04a0
4 changed files with 173 additions and 0 deletions

31
config/sample.yaml Normal file
View File

@ -0,0 +1,31 @@
# Пример конфигурационного файла для PGanec
servers:
- name: "PostgreSQL в k3s"
host: "postrges.local"
port: 5432
user: "postgres"
password: "****" # Здесь должен быть ваш пароль
- name: "PostgreSQL в Synology DS1522+ (Docker)"
host: "ds1522.local"
port: 5432
user: "postgres"
password: "****" # Здесь должен быть ваш пароль
- name: "Резервный сервер PostgreSQL"
host: "192.168.1.200"
port: 5432
user: "postgres"
password: "****" # Здесь должен быть ваш пароль
targets:
- mountpoint: "/mnt/backups"
label: "nas01"
type: "nfs"
- mountpoint: "/mnt/usb"
label: "usb-disk"
type: "ext4"

11
src/pganec/_init_.py Normal file
View File

@ -0,0 +1,11 @@
"""
PGanec - TUI для резервного копирования и восстановления баз данных PostgreSQL.
Основные возможности:
- Конфигурация через YAML-файл для описания доступных серверов и мест хранения резервных копий.
- Резервное копирование баз данных PostgreSQL.
- Восстановление баз данных PostgreSQL.
"""
__version__ = "0.1.0"
__author__ = "Sergey Erjemin"
__license__ = "MIT"

63
src/pganec/config.py Normal file
View File

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
import logging
import yaml
from typing import Optional, Dict # Для Python < 3.9 используйте Dict, для Python 3.9+ можно просто dict
logger = logging.getLogger(__name__)
class ConfigError(Exception):
"""Базовый класс для ошибок конфигурации."""
pass
class ConfigNotFoundError(ConfigError):
"""Файл конфигурации не найден."""
pass
class InvalidConfigFormatError(ConfigError):
"""Ошибка формата YAML в конфигурации."""
pass
def load_config(path: str) -> Optional[Dict]:
"""
Загрузка конфигурации из YAML файла.
Обрабатывает ошибки чтения файла и парсинга YAML.
:param path: Путь к файлу конфигурации.
:return: Словарь с конфигурацией или None в случае ошибки.
"""
logger.debug(f"Попытка загрузки конфигурации из файла: `{path}`")
try:
with open(path, "r", encoding="utf-8") as f:
config_data = yaml.safe_load(f)
# Проверим, что YAML действительно распарсился в словарь
if not isinstance(config_data, dict):
msg = f"Содержимое файла конфигурации `{path}` не является словарем. Тип: {type(config_data).__name__}."
logger.error(msg)
raise InvalidConfigFormatError(msg)
logger.info(msg=f"Конфигурация успешно загружена из `{path}`")
return config_data
except FileNotFoundError:
msg=f"Файл конфигурации не найден: `{path}`"
logger.error(msg)
raise ConfigNotFoundError(msg) from None # from None подавляет цепочку исключений
except yaml.YAMLError as e:
# YAMLError - базовый класс для ошибок PyYAML (ошибки сканера, парсера и т.п.)
msg = f"Ошибка парсинга YAML в файле `{path}`: {e}"
logger.error(msg)
raise InvalidConfigFormatError(msg) from e
except IOError as e:
# IOError или OSError для других проблем с доступом к файлу (например, права доступа)
msg = f"Ошибка ввода-вывода при чтении файла `{path}`: {e}"
logger.error(msg)
raise ConfigError(msg) from e # Общее исключение для других ошибок ввода-вывода
except Exception as e:
# Отлавливаем любые другие непредвиденные исключения
logger.critical(
msg=f"Непредвиденная ошибка при загрузке конфигурации из `{path}`: {e}",
exc_info=True # Добавляет трассировку стека в лог для лучшей диагностики
)
raise ConfigError(f"Непредвиденная ошибка: {e}") from e

68
src/pganec/main.py Normal file
View File

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
import argparse
import logging
import sys
from config import load_config, ConfigError, ConfigNotFoundError, InvalidConfigFormatError
# --- Настройки и инициирование логирования ---
logger = logging.getLogger(__name__)
def print_hi(name):
# Use a breakpoint in the code line below to debug your script.
print(f'Hi, {name}') # Press ⌘F8 to toggle the breakpoint.
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="PGanec — TUI резервного копирования и восстановления баз PostgreSQL")
parser.add_argument("-c", "--config", required=True,
help="Путь к файлу конфигурации (YAML)")
parser.add_argument("-d", "--debug-level", default="QUIET",
help="Уровень отладки (QUIET, NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL)")
parser.add_argument("-l", "--loging", default="",
help="Путь к файлу логирования (если не указан, логирование не будет вестись)")
args = parser.parse_args()
# Настройка отладочных логов
level_input_str = args.debug_level.upper()
chosen_log_level = None
if level_input_str == "QUIET": # Опция "QUIET"
chosen_log_level = logging.CRITICAL + 1
else:
chosen_log_level = getattr(logging, level_input_str, None)
if chosen_log_level is None:
logger.warning(f"Неизвестный уровень отладки '{args.debug_level}'. Используется DEBUG.")
chosen_log_level = logging.DEBUG
logging.basicConfig(
level=chosen_log_level,
format="%(asctime)s - %(name)s - %(levelname)s - %(module)s.%(funcName)s:%(lineno)d - %(message)s"
)
logger.debug(f"Параметрами: {args}")
logger.debug(f"Уровень: {chosen_log_level}")
# Загрузка конфигурации
try:
config = load_config(args.config)
logger.info(f"Конфигурация успешно загружена: {config}")
except ConfigNotFoundError:
print(f"Ошибка: Файл конфигурации '{args.config}' не найден.", file=sys.stderr)
# Возможно сюда стоит добавить TUI интерфейс для ввода (выбора) файла конфигурации...
# Но так как ошибка может быть связана с правами доступа (например, TUI запущен от неправильного
# пользователя), оставим идею "на-подумать", а пока просто завершим выполнение программы.
sys.exit(1)
except InvalidConfigFormatError as e:
print(f"Ошибка: Файл конфигурации '{args.config}' имеет неверный формат. {e}", file=sys.stderr)
sys.exit(1)
except ConfigError as e: # Общая ошибка конфигурации (должна быть последней в цепочке)
print(f"Ошибка при загрузке конфигурации: {e}", file=sys.stderr)
sys.exit(1)
print_hi('PyCharm')
# with open("config.yaml", "r") as f:
# config = yaml.safe_load(f)