Compare commits
7 Commits
d387d5849f
...
a8f094eb53
Author | SHA1 | Date |
---|---|---|
|
a8f094eb53 | |
|
6e258b03ba | |
|
c0f6a46f2a | |
|
bc27460564 | |
|
a40a1ce87d | |
|
0110d9438a | |
|
cd572b3c1a |
|
@ -10,4 +10,4 @@ app/config/
|
|||
!app/config/config-template.ini
|
||||
!app/config/.env-template
|
||||
app/backups/
|
||||
!app/backups/.keep
|
||||
!/app/backups/.keep
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,4 +8,10 @@ NET_DEV_PWD=password
|
|||
# название токена от для подключения к репозиторию
|
||||
GIT_USERNAME=git_user
|
||||
# токен для подключения к репозиторию
|
||||
GIT_TOKEN=bla-bla-lba
|
||||
GIT_TOKEN=bla-bla-lba
|
||||
|
||||
|
||||
# пользователь и пароль для отправки почты
|
||||
MAIL_USER=mail_user
|
||||
# токен для подключения к репозиторию
|
||||
MAIL_PWD=bla-bla-lba
|
|
@ -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 - включение\отключение отправки файлов в удалённый репозиторий
|
||||
|
|
18
app/main.py
18
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")
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue