tools.win_pg_dump_controller
2:7c93b0305522 Browse Files
+ Возможность отправлять оповещения по почте
README.md win_pg_dump_controller/__main__.py win_pg_dump_controller/config.py win_pg_dump_controller/log_controller.py win_pg_dump_controller/smtp_connector.py
1.1 --- a/README.md Fri Feb 11 23:55:12 2022 +0300 1.2 +++ b/README.md Thu May 05 22:25:18 2022 +0300 1.3 @@ -26,6 +26,7 @@ 1.4 1.5 * `main` - основная конфигурация скрипта 1.6 * `common` - общие параметры заданий 1.7 +* `smtp` - настройка оповещения через почту 1.8 * `${Имя задания}` - параметры задания. `${Имя задания}` задаётся пользователем, чтобы ему было понятно. 1.9 Оно фигурирует в журналах, используется в файлах резерных копий. Поэтому есть смысл избегать в нём русских букв 1.10 и специальных символов 1.11 @@ -45,6 +46,14 @@ 1.12 * `keep_logs_days` - количество дней за который хранить журналы. 1.13 1.14 1.15 +### Параметры в `smtp` 1.16 + 1.17 +* `smtp server` - IP или имя узла SMTP сервера 1.18 +* `smtp port` - Порт SMTP сервера 1.19 +* `mail from` - E-Mail источника почты 1.20 +* `mail to` - Имя ПЯ куда отправлять письма 1.21 + 1.22 + 1.23 ### Параметры `common` и раздела задач 1.24 1.25 * `host_name` - имя или IP узла баз данных. По умолчанию `127.0.0.1`
2.1 --- a/win_pg_dump_controller/__main__.py Fri Feb 11 23:55:12 2022 +0300 2.2 +++ b/win_pg_dump_controller/__main__.py Thu May 05 22:25:18 2022 +0300 2.3 @@ -4,6 +4,7 @@ 2.4 from .config import Config 2.5 from .executor import backup 2.6 from .error import Error 2.7 +from .smtp_connector import MailError, MailSender 2.8 2.9 config = Config() 2.10 log_controller = LogController(config) 2.11 @@ -24,6 +25,13 @@ 2.12 log_controller.clean() 2.13 log(log_t('Завершено')) 2.14 2.15 + if log_controller.main_log is not None: 2.16 + with open(log_controller.main_log) as IN: 2.17 + mail_content = IN.read() 2.18 + 2.19 + mail = MailSender.from_config(config) 2.20 + mail.sendmail(mail_content, logs=log_controller.log_files) 2.21 + 2.22 except Error as e: 2.23 log.err(str(e)) 2.24 print('FAIL')
3.1 --- a/win_pg_dump_controller/config.py Fri Feb 11 23:55:12 2022 +0300 3.2 +++ b/win_pg_dump_controller/config.py Thu May 05 22:25:18 2022 +0300 3.3 @@ -14,6 +14,7 @@ 3.4 3.5 COMMON_SECTION = 'common' 3.6 MAIN = 'main' 3.7 +SMTP = 'smtp' 3.8 DEFAULT_PG_PORT = 5432 3.9 3.10 3.11 @@ -74,7 +75,7 @@ 3.12 3.13 tmpl = cls(host_name=host_name, user_name=user_name, passwd=passwd, dst_dir=dst_dir, port=port) 3.14 3.15 - for _section in filter(lambda x: x not in (COMMON_SECTION, MAIN), config.sections()): 3.16 + for _section in filter(lambda x: x not in (COMMON_SECTION, MAIN, SMTP), config.sections()): 3.17 yield tmpl.parse_section(config[_section]) 3.18 3.19 3.20 @@ -97,6 +98,18 @@ 3.21 self.tier2_store_days = _main_section.getint('tier2_store_days', 30) 3.22 self.keep_logs_days = _main_section.getint('keep_logs_days', 30) 3.23 3.24 + self.smtp_server = None 3.25 + self.mail_from = None 3.26 + self.mail_to = None 3.27 + self.smtp_port = None 3.28 + 3.29 + if _config.has_section(SMTP): 3.30 + _smtp_section = _config[SMTP] 3.31 + self.smtp_server = _smtp_section.get('smtp server') 3.32 + self.mail_from = _smtp_section.get('mail from') 3.33 + self.mail_to = _smtp_section.get('mail to', fallback='root') 3.34 + self.smtp_port = _smtp_section.getint('smtp port', fallback=25) 3.35 + 3.36 if not isdir(self.pg_bin_path): 3.37 raise ConfigError(f'No valid directory with binnary files of PostgreSQL is set: {self.pg_bin_path}') 3.38
4.1 --- a/win_pg_dump_controller/log_controller.py Fri Feb 11 23:55:12 2022 +0300 4.2 +++ b/win_pg_dump_controller/log_controller.py Thu May 05 22:25:18 2022 +0300 4.3 @@ -141,6 +141,8 @@ 4.4 def __init__(self, config: Config): 4.5 self.log_dir = config.log_dir 4.6 self.keep_logs_days = config.keep_logs_days 4.7 + self.log_files = [] 4.8 + self.main_log = None 4.9 4.10 @staticmethod 4.11 def _get_timeprefix() -> str: 4.12 @@ -148,7 +150,15 @@ 4.13 4.14 def get_logger(self, name: str) -> BaseLogger: 4.15 if self.log_dir is not None: 4.16 - return FileLogger(p_join(self.log_dir, f'{self._get_timeprefix()} - {name}.log'), appname=name) 4.17 + _log_name = p_join(self.log_dir, f'{self._get_timeprefix()} - {name}.log') 4.18 + if name.lower() != 'main': 4.19 + self.log_files.append(_log_name) 4.20 + 4.21 + else: 4.22 + self.main_log = _log_name 4.23 + 4.24 + return FileLogger(_log_name, appname=name) 4.25 + 4.26 else: 4.27 return NullLogger(appname=name) 4.28
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/win_pg_dump_controller/smtp_connector.py Thu May 05 22:25:18 2022 +0300 5.3 @@ -0,0 +1,63 @@ 5.4 +# coding: utf-8 5.5 + 5.6 +from .config import Config 5.7 + 5.8 +from email.message import EmailMessage 5.9 +from smtplib import SMTP, SMTPException 5.10 +from typing import List, Optional 5.11 +from socket import gethostname 5.12 +from datetime import datetime 5.13 +from os.path import basename, exists 5.14 + 5.15 + 5.16 +class MailError(Exception): 5.17 + pass 5.18 + 5.19 + 5.20 +class MailSender(object): 5.21 + def __init__(self, 5.22 + server: Optional[str], port: Optional[int], 5.23 + mail_from: Optional[str], mail_to: Optional[str]): 5.24 + self.server = server 5.25 + self.mail_from = mail_from 5.26 + self.mail_to = mail_to if mail_to is not None else 'root' 5.27 + self.port = port if port is not None else 25 5.28 + 5.29 + def sendmail(self, content: str, logs: List[str]) -> None: 5.30 + if self.server: 5.31 + msg = EmailMessage() 5.32 + msg['To'] = self.mail_to 5.33 + if self.mail_from: 5.34 + msg['From'] = self.mail_from 5.35 + 5.36 + msg['Subject'] = f'{gethostname()}: Posgres backup - {datetime.now()}' 5.37 + msg.set_content(content) 5.38 + msg.make_mixed() 5.39 + for f in logs: 5.40 + try: 5.41 + if exists(f): 5.42 + msg.add_attachment(open(f).read(), filename=basename(f)) 5.43 + 5.44 + except IOError as e: 5.45 + raise MailError(f'Error on open logfile "{f}": {e}') 5.46 + 5.47 + try: 5.48 + s = SMTP(host=self.server, port=self.port) 5.49 + s.helo(gethostname()) 5.50 + s.sendmail( 5.51 + from_addr=(self.mail_from if self.mail_from is not None else ''), 5.52 + to_addrs=self.mail_to, 5.53 + msg=msg.as_bytes() 5.54 + ) 5.55 + s.quit() 5.56 + except SMTPException as e: 5.57 + raise MailError(f'Error on sending Email: {e}') 5.58 + 5.59 + @classmethod 5.60 + def from_config(cls, config: Config): 5.61 + return cls( 5.62 + server=config.smtp_server, 5.63 + mail_from=config.mail_from, 5.64 + mail_to=config.mail_to, 5.65 + port=config.smtp_port 5.66 + ) 5.67 \ No newline at end of file