diff --git a/.gitignore b/.gitignore index 94fddc7..b033878 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,4 @@ app/config/ !app/config/config-template.ini !app/config/.env-template app/backups/ -!app/backups/.keep +!/app/backups/.keep diff --git a/README.md b/README.md index 2c5d78f..f843e97 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,8 @@ Python 3.13 - "netmiko>=4.5.0" - для подключения к сетевым устройствам - "schedule>=1.2.2" - для периодического выполнения backup - Для запуска. Переменные окружения (см. app/config/.env-template) app/config/config.ini Пример конфиг файла см. app/config/config-template.ini - +Положить файл со списком устройств в папку config \ No newline at end of file diff --git a/app/backup.py b/app/backup.py index 693fadb..a4702f8 100644 --- a/app/backup.py +++ b/app/backup.py @@ -36,7 +36,7 @@ def read_device_list() -> dict[str, dict[str, str]]: return all_devices -def connect_to_device(vendor: str, host: str) -> ConnectHandler: +def connect_to_device(vendor: str, host: str, name: str="") -> ConnectHandler: conn_conf: dict = { "host": host, "username": cfg.net_dev.user, @@ -61,14 +61,14 @@ def connect_to_device(vendor: str, host: str) -> ConnectHandler: conn_conf["username"] = cfg.net_dev.domain + "\\" + cfg.net_dev.user try: connection: ConnectHandler = ConnectHandler(**conn_conf) - log.info("connect to %r", host) + log.info("connect to %s %s", name, host) return connection except exceptions.NetmikoAuthenticationException: - log.warning("Authentication error %r ", host) + log.warning("Authentication error %s %s ", name, host) except exceptions.NetmikoTimeoutException: - log.warning("Connection time out error %r ", host) + log.warning("Connection time out error %s %s ", name, host) except Exception as e: - log.warning("Connection error %r: %r", host, e) + log.warning("Connection error %s %s: %s", name, host, e) def send_command(connection: ConnectHandler, command: str) -> str: @@ -84,6 +84,7 @@ def save_mikrotik_bcp(host: str, name: str) -> None: connection = connect_to_device( vendor="mikrotik", host=host, + name=name, ) if connection is None: return @@ -112,7 +113,7 @@ def save_snr_bcp(host: str, name: str) -> None: connection.disconnect() log.info("disconnected from %r", name) if result == "NetmikoTimeoutException": - log.warning("Timeout read config from %r", name) + log.warning("Timeout read config from %s", name) return with open(os.path.join(cfg.bcp.dir, name), "w") as f: f.write(result) @@ -130,14 +131,13 @@ def save_cisco_sb_bcp(host: str, name: str) -> None: return result = send_command(connection, "show running-config") connection.disconnect() - log.info("disconnected from %r", name) + log.info("disconnected from %s", name) if result == "NetmikoTimeoutException": - log.warning("Timeout read config from %r", name) + log.warning("Timeout read config from %s", name) return with open(os.path.join(cfg.bcp.dir, name), "w") as f: f.write(result) log.info("Backup saved") - add_file_and_commit(file_name=name) @@ -157,5 +157,4 @@ def save_cisco_bcp(host: str, name: str) -> None: with open(os.path.join(cfg.bcp.dir, name), "w") as f: f.write(result) log.info("Backup saved") - add_file_and_commit(file_name=name) diff --git a/app/config.py b/app/config.py index eab0ed8..32960c4 100644 --- a/app/config.py +++ b/app/config.py @@ -1,6 +1,5 @@ from dataclasses import dataclass import logging - import configparser import os @@ -81,6 +80,19 @@ class ConfigGit: username: str = os.getenv("GIT_USERNAME") token: str = os.getenv("GIT_TOKEN") +@dataclass +class ConfigMail: + send: bool = config["mail"].getboolean("send") + if send: + user: str = os.getenv('MAIL_USER') + pwd: str = os.getenv('MAIL_PWD') + server: str = config['mail'].get('server') + port: int = config['mail'].getint('port') + from_address : str = config['mail'].get('from_address') + to_address : str = config['mail'].get('to_address') + subject : str = config['mail'].get('subject') + + @dataclass() class ConfigAll: @@ -89,6 +101,7 @@ class ConfigAll: bcp: ConfigBcp = ConfigBcp net_dev: ConfigNetDev = ConfigNetDev git: ConfigGit = ConfigGit + mail: ConfigMail = ConfigMail cfg = ConfigAll diff --git a/app/config/.env-template b/app/config/.env-template index 0f6963c..441161d 100644 --- a/app/config/.env-template +++ b/app/config/.env-template @@ -8,4 +8,10 @@ NET_DEV_PWD=password # название токена от для подключения к репозиторию GIT_USERNAME=git_user # токен для подключения к репозиторию -GIT_TOKEN=bla-bla-lba \ No newline at end of file +GIT_TOKEN=bla-bla-lba + + +# пользователь и пароль для отправки почты +MAIL_USER=mail_user +# токен для подключения к репозиторию +MAIL_PWD=bla-bla-lba \ No newline at end of file diff --git a/app/config/config-template.ini b/app/config/config-template.ini index 0fb05e8..a404ddb 100644 --- a/app/config/config-template.ini +++ b/app/config/config-template.ini @@ -3,6 +3,16 @@ # False \ True - включение\отключение отправки оповещения в телеграм send: False + +[mail] +# False \ True - включение\отключение отправки оповещения на почту +send: False +server: smtp.example.com +port: 25 +from_address: net-bcp@example.com +to_address: engineer@example.com, network_manager@example.com +subject: Warning in backup network device + # Конфиг гит репозитория [git] # False \ True - включение\отключение отправки файлов в удалённый репозиторий diff --git a/app/main.py b/app/main.py index 6f39de6..dd3a05f 100644 --- a/app/main.py +++ b/app/main.py @@ -7,14 +7,22 @@ from backup import ( save_cisco_sb_bcp, save_cisco_bcp, ) +from messages import send_mail from repo import check_bcp_repo, push_to_remote import schedule import time - +import os def main(): check_bcp_repo() device_dict = read_device_list() + log_dir: str = os.path.join(os.path.abspath(os.path.dirname(__file__)), "logs") + log_file: str = os.path.join(log_dir, "logfile.log") + if os.path.isfile(log_file): + with open(log_file, 'w') as file: + file.write('') + log.info("log file clear") + for device in device_dict: current_device = device_dict[device] if current_device["vendor"].lower() == "mikrotik": @@ -40,6 +48,12 @@ def main(): save_cisco_bcp(host=current_device["ip"], name=current_device["name"]) if cfg.git.push: push_to_remote() + if cfg.mail.send: + if os.path.isfile(log_file): + with open(log_file, 'r', encoding='utf-8') as file: + text = file.read() + send_mail(text) + if __name__ == "__main__": @@ -50,7 +64,7 @@ if __name__ == "__main__": main() while True: schedule.run_pending() - time.sleep(60) + time.sleep(120) except KeyboardInterrupt: log.info("Manual app stopped") log.info("App stopped") diff --git a/app/messages.py b/app/messages.py new file mode 100644 index 0000000..77ed710 --- /dev/null +++ b/app/messages.py @@ -0,0 +1,23 @@ +import smtplib +from email.message import EmailMessage +import logging as log +from config import cfg + +def send_mail(text: str) -> None: + log.info('sending a message') + msg = EmailMessage() + msg['Subject'] = cfg.mail.subject + msg['From'] = cfg.mail.from_address + msg['To'] = cfg.mail.to_address + msg.set_content(text) + try: + smtp = smtplib.SMTP(cfg.mail.server, cfg.mail.port) + smtp.starttls() + smtp.ehlo() + smtp.login(cfg.mail.user, cfg.mail.pwd) + + smtp.send_message(msg) + smtp.quit() + log.info('message sent successfully') + except smtplib.SMTPException as err: + log.warning(err) diff --git a/app/repo.py b/app/repo.py index 81d074e..1964383 100644 --- a/app/repo.py +++ b/app/repo.py @@ -1,6 +1,7 @@ from git import Repo, InvalidGitRepositoryError, Git from config import cfg import logging as log +from datetime import datetime def check_remote_repo(repo: Repo): @@ -20,7 +21,6 @@ def check_remote_repo(repo: Repo): log.info( "merge from remote repo ", ) - else: remote = repo.remote(name=cfg.git.branch) remote.set_url( @@ -48,21 +48,21 @@ def check_bcp_repo(path: str = cfg.bcp.dir) -> None: config.set_value("user", "email", cfg.git.mail) if cfg.git.push: check_remote_repo(repo) - except Exception as e: log.warning("Error: %r", e) def add_file_and_commit(file_name: str) -> None: + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") repo = Repo(cfg.bcp.dir) if file_name in repo.untracked_files: repo.git.add(file_name) + repo.index.commit("add %s %s" % (file_name, current_time)) log.info("%r added in repo", file_name) - repo.index.commit("add file %s" % file_name) if bool(repo.git.diff(file_name)): repo.index.add([file_name]) - repo.index.commit("update file %s" % file_name) + repo.index.commit("update %s %s" % (file_name, current_time)) log.info("change in %r commited", file_name) else: log.info("no changed in file %r", file_name)