add: read args & parse config
This commit is contained in:
parent
327e0a55e9
commit
4a81ed04a0
31
config/sample.yaml
Normal file
31
config/sample.yaml
Normal 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
11
src/pganec/_init_.py
Normal 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
63
src/pganec/config.py
Normal 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
68
src/pganec/main.py
Normal 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)
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user