Compare commits

...

107 Commits

Author SHA1 Message Date
122ef64ad7 build 2025-09-09 11:20:50 +02:00
99f673996e build 2025-09-03 00:44:11 +02:00
68e23da03b build 2025-09-03 00:42:59 +02:00
1c30fb9995 build 2025-09-03 00:42:34 +02:00
b94b61ba5d build 2025-09-03 00:42:04 +02:00
26c3245cf3 build 2025-09-03 00:41:26 +02:00
1510dc2e8d build 2025-09-03 00:40:13 +02:00
d949188a34 build 2025-09-03 00:39:24 +02:00
298c9aee23 build 2025-09-02 22:52:54 +02:00
808f92c4d1 build 2025-09-02 22:50:36 +02:00
43f6d2abcb build 2025-09-02 22:47:01 +02:00
81c7ead7b2 build 2025-09-02 22:44:12 +02:00
cd818da774 build 2025-09-02 22:26:51 +02:00
a1dfbd664c build 2025-09-02 22:23:40 +02:00
571219881d build 2025-09-01 14:01:43 +02:00
0c07fde85a build 2025-09-01 13:58:44 +02:00
f46eacf627 build 2025-09-01 13:54:13 +02:00
92226734ef build 2025-09-01 13:52:25 +02:00
d3359e9a68 build 2025-09-01 13:49:24 +02:00
fdbe4eebe1 build 2025-09-01 13:32:58 +02:00
3a3faad97e build 2025-09-01 13:22:21 +02:00
8be3e20523 build 2025-09-01 13:20:29 +02:00
126ab1813b build 2025-09-01 13:19:35 +02:00
28efc95b4d build 2025-09-01 13:18:50 +02:00
f6a106fd91 build 2025-09-01 13:17:03 +02:00
bbe4d72666 build 2025-09-01 12:47:25 +02:00
b536c8ecb1 build 2025-09-01 12:37:22 +02:00
7a8130c3f0 build 2025-09-01 12:35:46 +02:00
5756798269 build 2025-09-01 12:34:02 +02:00
d6af0c24b5 build 2025-09-01 12:30:32 +02:00
6f9a2bba67 build 2025-09-01 12:24:42 +02:00
11bc56ecb3 build 2025-09-01 12:23:17 +02:00
b4032eca7e Merge branch 'main' of gitlab.sectorq.eu:jaydee/omv_backup 2025-09-01 12:22:38 +02:00
f75ac2eb79 build 2025-09-01 12:22:00 +02:00
a555567c4b Update .gitlab-ci.yml file 2025-09-01 12:21:49 +02:00
de63a1e9aa build 2025-09-01 12:19:30 +02:00
d3eab9f50e build 2025-09-01 12:15:01 +02:00
72a2fa5710 build 2025-09-01 10:36:56 +02:00
b3be50bfdd build 2025-09-01 10:27:40 +02:00
f32ada9ad5 build 2025-09-01 10:16:52 +02:00
d6492ebf80 build 2025-08-27 04:38:57 +02:00
7cee7570b4 added v3 2025-08-27 04:37:26 +02:00
ce7c855808 added v3 2025-08-27 04:35:27 +02:00
d3e15e973a build 2025-08-26 16:00:01 +02:00
cee1cf353c build 2025-08-26 15:58:31 +02:00
f9e7654c47 build 2025-08-26 15:51:51 +02:00
1816bd516c build 2025-08-26 15:43:06 +02:00
dad1c49409 build 2025-08-26 15:38:20 +02:00
66644b6bb9 build 2025-08-26 15:30:19 +02:00
781a74ba20 build 2025-08-26 15:27:23 +02:00
29cb028c5c build 2025-08-26 15:24:05 +02:00
1667ce0079 build 2025-08-26 15:18:01 +02:00
984b3e09a0 build 2025-08-26 15:15:26 +02:00
64bea0a833 build 2025-08-26 15:07:51 +02:00
dd51f14699 build 2025-08-26 15:04:35 +02:00
35aa35a3b5 build 2025-08-26 14:57:00 +02:00
4f868047be build 2025-08-26 14:48:39 +02:00
ba09aeb502 build 2025-08-26 13:38:19 +02:00
8e9e915cdc build 2025-08-26 13:35:58 +02:00
1714817673 build 2025-08-26 12:40:37 +02:00
9cf123382e build 2025-08-26 12:38:36 +02:00
ad5e9e698f build 2025-08-26 12:36:32 +02:00
017653d34c build 2025-08-26 12:27:39 +02:00
cb2e06684b build 2025-08-26 12:21:53 +02:00
57ae295d22 build 2025-08-26 12:19:11 +02:00
27d0e121cd build 2025-08-26 12:14:18 +02:00
de97d2574a build 2025-08-26 12:12:17 +02:00
ca1c3a563f build 2025-08-26 11:59:19 +02:00
0d6ec1fbc1 build 2025-08-26 11:52:34 +02:00
a11076ef04 build 2025-08-26 11:52:00 +02:00
d6a3d3765b build 2025-08-26 11:49:23 +02:00
eababc2f7c build 2025-08-26 11:47:22 +02:00
528995cf75 build 2025-08-26 11:45:38 +02:00
695219f6e0 build 2025-08-26 11:43:14 +02:00
d6e41e2ceb build 2025-08-26 11:42:09 +02:00
0e6ebc0516 build 2025-08-26 11:38:42 +02:00
7c2c99fbef build 2025-08-26 11:15:05 +02:00
425f9aebaa build 2025-08-26 10:52:52 +02:00
f8ae15d195 build 2025-08-26 10:36:01 +02:00
c5d915ad87 build 2025-08-26 10:35:02 +02:00
9e7b774601 build 2025-08-26 10:34:17 +02:00
0eb8cc4afe build 2025-08-26 09:33:50 +02:00
ea1d933386 build 2025-08-26 09:29:46 +02:00
a407ee9339 build 2025-08-26 09:04:03 +02:00
1768fe1369 build 2025-08-26 09:03:02 +02:00
2c7dba48da build 2025-08-26 09:01:13 +02:00
bcd2b5de9e build 2025-08-26 09:01:00 +02:00
986f75e1dc build 2025-08-26 08:57:47 +02:00
2af8758261 build 2025-08-26 08:43:36 +02:00
fd94729a05 build 2025-08-23 11:43:33 +02:00
fb4acad563 build 2025-08-21 13:00:22 +02:00
a57c73cc74 build 2025-08-21 12:13:54 +02:00
4327d7490b build 2025-08-21 11:50:34 +02:00
c72aa231c7 build 2025-08-21 11:45:53 +02:00
169d43a75c build 2025-08-21 10:11:45 +02:00
559228d2e3 build 2025-08-19 16:54:23 +02:00
20f380647f build 2025-08-19 16:16:58 +02:00
5b24664f46 build 2025-08-02 18:01:16 +02:00
0bf0aef1e2 build 2025-08-02 18:00:37 +02:00
c93a32a79f build 2025-08-02 17:00:21 +02:00
c0213d97e1 build 2025-08-02 16:25:16 +02:00
edde137ae6 build 2025-08-02 16:24:25 +02:00
e476237ec2 build 2025-08-02 16:23:40 +02:00
0ffd2adf64 build 2025-08-02 16:23:18 +02:00
ac0089f205 build 2025-08-02 16:17:12 +02:00
81ffc7a72b build 2025-08-02 16:16:50 +02:00
9ef0cc9dac build 2025-07-17 09:26:03 +02:00
65 changed files with 412 additions and 275 deletions

View File

@@ -25,6 +25,6 @@ build-job: # This job runs in the build stage, which runs first.
script:
- column=":"
- echo "${flow_id}"
- curl -X POST https://kestra.sectorq.eu/api/v1/executions/webhook/jaydee/ansible-all/${flow_id} -d '{"tag":["setup","omv_backup"],"target":["servers"]}' -H "Content-Type${column} application/json"
- curl -X POST https://kestra.sectorq.eu/api/v1/executions/webhook/jaydee/ansible-all/${flow_id} -d '{"tag":["omv_backup"],"target":["servers"]}' -H "Content-Type${column} application/json"
rules:
- if: '$CI_COMMIT_MESSAGE =~ /build/'

View File

@@ -47,6 +47,13 @@ cmnd = f"zip -P {PASSWORD} authentik_{DATETIME}.sql.zip authentik_{DATETIME}.sql
status, output = subprocess.getstatusoutput(cmnd)
os.remove(f"authentik_{DATETIME}.sql")
print("Backup Zabbix")
cmnd = f"docker exec zabbix-server-db-server-1 sh -c 'pg_dump -h localhost -p 5432 -U zabbix -d zabbix' > /share/docker_data/__backups/zabbix_{DATETIME}.sql"
status, output = subprocess.getstatusoutput(cmnd)
cmnd = f"zip -P {PASSWORD} zabbix_{DATETIME}.sql.zip zabbix_{DATETIME}.sql"
status, output = subprocess.getstatusoutput(cmnd)
os.remove(f"zabbix_{DATETIME}.sql")
print("Backup portainer")
headers = {

15
exclude.txt Normal file
View File

@@ -0,0 +1,15 @@
ha/home-assistant_v2.db
jellyfin/cache/transcodes
.@__thumb/*
ha/home-assistant.log
gitlab/logs/*
esphome/config/.esphome
.cache
.git
var_lib_motioneye/*
*/.esphome/build/*
nextcloud/mariadb/*
zabbix-server/postgres-data/*
gitea-runner/*
immich/library/*

View File

@@ -16,11 +16,22 @@ import requests
import fnmatch
import yaml
import paramiko
import shutil
import signal
import paho.mqtt.client as mqtt
#import numpy as np
def signal_handler(sig, frame):
print('You pressed Ctrl+C!')
conn.close()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
file_path = os.path.realpath(__file__)
dir_path = os.path.dirname(file_path)
VERSION="1.0.8"
VERSION="1.0.10"
# print(file_path)
# print(dir_path)
os.chdir(dir_path)
@@ -41,6 +52,7 @@ status, output = subprocess.getstatusoutput(cmnd)
if int(output) > 0:
print("Running already!")
sys.exit()
def is_port_open(host, port):
try:
sock = socket.create_connection((host, port))
@@ -98,9 +110,6 @@ for o, a in opts:
elif o in ("-r", "--restore"):
_RESTORE = True
_APP = a
print("RESTORE")
elif o in ("-b", "--backup"):
_BACKUP = True
elif o in ("-D", "--dry"):
_EXECUTE = False
elif o in ("-T", "--dry"):
@@ -124,13 +133,23 @@ logging.info("script started")
logger = logging.getLogger(__name__)
client_id = "dasdasdasd333"
try:
client = mqtt_client.Client(mqtt_client.CallbackAPIVersion.VERSION1, client_id)
except:
client = mqtt_client.Client()
client.username_pw_set(mqtt_username, mqtt_password)
# try:
# client = mqtt_client.Client(mqtt_client.CallbackAPIVersion.VERSION1, client_id)
# except:
# client = mqtt_client.Client()
# client.username_pw_set(mqtt_username, mqtt_password)
logging.info(f"Start OMV Backup")
BROKER = 'mqtt.home.lan' # e.g., 'mqtt.example.com'
PORT = 1883 # Typically 8883 for secure MQTT
TOPIC = 'sectorq/backups/start'
USERNAME = 'jaydee'
PASSWORD = 'jaydee1'
USE_TLS = False # Set to False if not using TLS
backups = {
"nas": {
"login": "admin@nas.home.lan",
@@ -184,11 +203,14 @@ logging.info("Test connection")
hm = socket.gethostbyaddr(BACKUP_HOST)
logging.info(_RESTORE)
def send_mqtt_message(msg):
def send_mqtt_message(topic,msg,qos=0,retain=False):
client2 = mqtt.Client()
client2.username_pw_set("jaydee", "jaydee1")
try:
client.connect(broker,1883,60)
client.publish(topic, json.dumps(msg))
client.disconnect()
client2.connect("mqtt.home.lan",1883,60)
client2.publish(topic, json.dumps(msg), qos=qos, retain=retain)
client2.disconnect()
logging.info(f"Message1 sent {topic}, {msg}")
except ValueError as e:
logging.error("Failed to send")
print("Failed to send")
@@ -217,11 +239,13 @@ if _STOP:
continue
cmnd = f"docker stop {c.split()[-1]}"
status, running_containers = subprocess.getstatusoutput(cmnd)
if _RESTORE:
def restore_job(_APP):
#global VERSION
logging.info("Starting Restore")
print("Starting Restore")
print(f"Starting restore : {VERSION}")
now = datetime.datetime.now()
STARTTIME = now.strftime("%Y-%m-%d_%H:%M:%S")
_DATE = "pick"
if _APP == "all":
_DATE = "latest"
if host == "rpi5.home.lan" or host == "rpi5":
@@ -234,16 +258,17 @@ if _RESTORE:
#input("????")
else:
_APP = _APP.split(",")
PROGRESS = 0
topic = "sectorq/amd/restore"
step = 100 / len(_APP)
for app in _APP:
#msg = {"mode":_MODE, "status":"restore","bak_name":"Restore","host":host,"cur_job":app,"start_time":STARTTIME,"end_time":"","progress":str(round(np.ceil(PROGRESS))) + "%","finished":1,"used_space":1}
msg = {"mode":_MODE, "status":"restore","bak_name":"Restore","host":host,"cur_job":app,"start_time":STARTTIME,"end_time":"","progress":"0" + "%","finished":1,"used_space":1}
msg = {"mode":_MODE, "status":"restore","bak_name":"Restore","host":host,"cur_job":app,"start_time":STARTTIME,"end_time":"","progress":str(round(PROGRESS,0)) + "%","finished":1,"used_space":1}
logging.info(msg)
send_mqtt_message(msg)
send_mqtt_message(topic,msg)
PROGRESS = PROGRESS + step
now = datetime.datetime.now()
DATETIME = now.strftime("%Y-%m-%d_%H-%M-%S")
@@ -255,7 +280,7 @@ if _RESTORE:
if _DATE == "pick":
cmnd = f"ssh root@amd.home.lan 'ls {BACKUP_DEVICE}/backup/m-server/docker_data'"
status, output = subprocess.getstatusoutput(cmnd)
print(output)
# print(output)
dates = output.splitlines()
n = 1
for i in dates:
@@ -282,7 +307,7 @@ if _RESTORE:
LATEST_LINK = f"/{host}/{app}/{_DATE}"
logging.info("Create backup dir")
logging.info(cmnd)
#logging.info(cmnd)
#cmnd = "rsync -av --delete {}/ --link-dest {} --exclude=\".cache\" {}".format(SOURCE_DIR, LATEST_LINK, BACKUP_PATH)
@@ -451,268 +476,305 @@ if _RESTORE:
msg = {"mode":_MODE, "status":"restore","bak_name":"Restore","host":host,"cur_job":app,"start_time":STARTTIME,"end_time":"","progress":100,"finished":ENDJOB,"used_space":1}
logging.info(msg)
send_mqtt_message(msg)
send_mqtt_message(topic,msg)
if _MODE == "auto":
cmnd = "ssh root@amd.home.lan 'systemctl suspend &'"
status, output = subprocess.getstatusoutput(cmnd)
if _BACKUP:
last = 1
while True:
directory = '/backups/'
count = len(fnmatch.filter(os.listdir(directory), '*'))
if last != count:
logging.info(f'File Count: {count}')
last = count
if count == 0:
time.sleep(10)
continue
else:
finished = []
now = datetime.datetime.now()
STARTTIME = now.strftime("%Y-%m-%d_%H:%M:%S")
topic = "sectorq/amd/backups"
msg = {"mode":_MODE, "status":"started","bak_name":"complete","host":"","cur_job":"","start_time":STARTTIME,"end_time":"in progress","progress":0,"finished":",".join(finished)}
send_mqtt_message(msg)
# iterate over files in
# that directory
for filename in os.scandir(directory):
if filename.is_file():
logging.info(filename.path)
logging.info(filename.name)
if filename.name == "restore":
break
host = filename.name
logging.info("Backup")
for b in backups[host]["jobs"]:
if not backups[host]["jobs"][b]["active"]:
logging.info("Backup {} is not active!".format(b))
msg = {"status":"inactive","bak_name":b,"start_time":"inactive","end_time":"inactive","progress":0}
send_mqtt_message(msg)
continue
SOURCE_DIR = backups[host]["jobs"][b]["source"]
now = datetime.datetime.now()
BACKUP_HOST = backups[host]["login"]
BACKUP_DEVICE = "/mnt/raid"
BACKUP_DIR = f"{BACKUP_HOST}:{SOURCE_DIR}"
BACKUP_ROOT = f"{BACKUP_DEVICE}/backup/{host}/{b}"
DATETIME = now.strftime("%Y-%m-%d_%H-%M-%S")
if _FIRST:
NEW_BACKUP_DIR = f"{BACKUP_ROOT}/initial"
else:
NEW_BACKUP_DIR = f"{BACKUP_ROOT}/{DATETIME}_running"
FULL_BACKUP_LATEST = f"{BACKUP_ROOT}/latest"
# msg = {"status":"started","bak_name":b,"start_time":DATETIME,"end_time":"in progress", "progress":0}
msg = {"mode":_MODE, "status":"started","bak_name":"complete","host":host,"cur_job":b,"start_time":STARTTIME,"end_time":"in progress","progress":0,"finished":",".join(finished)}
client.connect(broker,1883,60)
client.publish(topic, json.dumps(msg),qos=0, retain=True)
client.disconnect()
cmnd = "mkdir -p " + NEW_BACKUP_DIR
logging.info(cmnd)
if _EXECUTE:
status, output = subprocess.getstatusoutput(cmnd)
logging.info(output)
logging.info(status)
logging.info("Create backup dir")
cmnd = f"ssh {BACKUP_HOST} 'ls {SOURCE_DIR}'"
logger.debug(cmnd)
status, output = subprocess.getstatusoutput(cmnd)
logger.debug(output)
apps = output.splitlines()
c = len(apps)
print(apps)
print(len(apps))
step = round(100 / c,1)
progress = 0
#cmd = f"rsync -avz --delete {BACKUP_DIR} --link-dest {FULL_BACKUP_LATEST}/ --exclude=\"jellyfin/cache/transcodes\" --exclude=\"gitlab/logs/prometheus\" --exclude=\"home-assistant.log\" --exclude=\"gitlab/logs/*\" --exclude=\"esphome/config/.esphome\" --exclude=\".cache\" --exclude=\".git\" --exclude=\"var_lib_motioneye\" {NEW_BACKUP_DIR}"
#cmd = [ 'rsync', '-avz','--info=progress2', BACKUP_DIR , NEW_BACKUP_DIR]
cmd = ['rsync', '-avz', '--delete', BACKUP_DIR, '--link-dest', FULL_BACKUP_LATEST, '--exclude="jellyfin/cache/transcodes"', '--exclude=".@__thumb/"', '--exclude="gitlab/logs/prometheus"', '--exclude="home-assistant.log"', '--exclude="gitlab/logs/*"', '--exclude="esphome/config/.esphome"', '--exclude=".cache"', '--exclude=".git"', '--exclude="var_lib_motioneye"', NEW_BACKUP_DIR]
logging.info(" ".join(cmd))
process = subprocess.Popen(cmd,
stdout=subprocess.PIPE)
while process.poll() is None:
line = process.stdout.readline().decode("utf-8").split("/")
print(line[0])
if line[0] in apps:
logging.info(f"Working on app {line[0]}")
while True:
if line[0] != apps[0]:
del apps[0]
progress = progress + step
else:
break
apps.remove(line[0])
#print(len(apps))
topic = "sectorq/amd/backups"
msg = {"mode":_MODE, "status":"started","bak_name":"complete","host":host,"cur_job":b,"sub":line[0],"start_time":STARTTIME,"end_time":"in progress","progress":str(round(progress)) + "%","finished":",".join(finished)}
send_mqtt_message(msg)
progress = progress + step
# input(apps)
# for a in apps:
# logging.info(f"App {a}")
# topic = "sectorq/amd/backups"
# msg = {"mode":_MODE, "status":"started","bak_name":"complete","host":host,"cur_job":b,"sub":a,"start_time":STARTTIME,"end_time":"in progress","progress":round(progress),"finished":",".join(finished)}
# send_mqtt_message(msg)
# logger.debug(cmnd)
# if _FIRST:
# cmnd = f"rsync -avz --delete {SOURCE_DIR} --exclude=\"jellyfin/cache/transcodes\" --exclude=\"gitlab/logs/prometheus\" --exclude=\"home-assistant.log\" --exclude=\"gitlab/logs/*\" --exclude=\"esphome/config/.esphome\" --exclude=\".cache\" --exclude=\".git\" --exclude=\"var_lib_motioneye\" rsync://{BACKUP_HOST}{BACKUP_PATH}"
# else:
# cmnd = f"rsync -avz --delete {BACKUP_DIR}{a} --link-dest {FULL_BACKUP_LATEST}/{a} --exclude=\"jellyfin/cache/transcodes\" --exclude=\"gitlab/logs/prometheus\" --exclude=\"home-assistant.log\" --exclude=\"gitlab/logs/*\" --exclude=\"esphome/config/.esphome\" --exclude=\".cache\" --exclude=\".git\" --exclude=\"var_lib_motioneye\" {NEW_BACKUP_DIR}"
# ans = "y"
# logging.info(cmnd)
# logging.info("Sync files1")
# #input("??????")
# if _TEST:
# ans = input("continue?") or "n"
# if ans == "y" and _EXECUTE:
# status, output = subprocess.getstatusoutput(cmnd)
# #proc = subprocess.Popen(cmnd,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,cwd = "/myapps/",shell=True)
# progress = progress + step
# topic = "sectorq/amd/backups"
# msg = {"mode":_MODE, "status":"started","bak_name":"complete","host":host,"cur_job":b,"sub":a,"start_time":STARTTIME,"end_time":"in progress","progress":round(progress),"finished":",".join(finished)}
# send_mqtt_message(msg)
cmnd = f"rm -rf {FULL_BACKUP_LATEST}"
logging.info(cmnd)
logging.info("Removing latest link")
# input("????")
if _EXECUTE:
status, output = subprocess.getstatusoutput(cmnd)
if _FIRST:
cmnd = f"cd {BACKUP_ROOT}; ln -s initial latest"
else:
cmnd = f"cd {BACKUP_ROOT}; mv {DATETIME}_running {DATETIME};ln -s {DATETIME} latest"
logging.info("Creating new latest link")
#print(cmnd)
# input("????")
if _EXECUTE:
status, output = subprocess.getstatusoutput(cmnd)
#Remove old
logging.info("Removing old dirs")
# input("????")
#cmnd = "find {} -maxdepth 1 -type d -mtime +30 -exec rm -rf {{}} \;".format(BACKUP_DIR)
cmnd = f"cd {BACKUP_ROOT} find ./ -maxdepth 1 -type d -mmin +30 -exec rm -rf {{}} \\;"
#print(cmnd)
# input("????")
if _EXECUTE:
status, output = subprocess.getstatusoutput(cmnd)
now = datetime.datetime.now()
ENDTIME = now.strftime("%Y-%m-%d_%H:%M:%S")
#msg = {"status":"finished","bak_name":b,"start_time":DATETIME,"end_time":ENDTIME,"progress":0}
finished.append(b)
msg = {"mode":_MODE, "status":"finished","bak_name":"complete","host":host,"cur_job":b,"start_time":ENDTIME,"end_time":"in progress","progress":0,"finished":",".join(finished)}
send_mqtt_message(msg)
logging.info("Getting size of FS")
cmnd = "df -h /mnt/raid|awk '{ print $3 }'|tail -1"
logging.info(cmnd)
status, output = subprocess.getstatusoutput(cmnd)
used_space = (output.split())[0]
now = datetime.datetime.now()
ENDJOB = now.strftime("%Y-%m-%d_%H:%M:%S")
logging.info("Size : {}".format(used_space))
logging.info("Sending finished status")
#msg = {"mode":_MODE,"status":"finished","bak_name":"complete","start_time":STARTTIME,"end_time":ENDJOB,"progress":0,"used_space":used_space}
msg = {"mode":_MODE, "status":"finished","bak_name":"complete","host":host,"cur_job":b,"start_time":STARTTIME,"end_time":ENDTIME,"progress":0,"finished":",".join(finished),"used_space":used_space}
logging.info(msg)
send_mqtt_message(msg)
os.remove(filename.path)
def backup_job(pl):
client2 = mqtt.Client()
client2.username_pw_set("jaydee", "jaydee1")
client2.connect("mqtt.home.lan",1883,60)
if "log" in pl:
if pl["log"] == "debug":
logging.info(f'Debug enabled')
LOG_FILE = "omv_backup.log"
logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG, format='%(asctime)s : %(levelname)s : %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.info(f'starting backup job')
server = pl["host"]
if pl["mode"] == "dry":
_DRYRUN = True
logging.info("Dry run active")
else:
_DRYRUN = False
logging.info("Full mode active")
finished = []
sub_finished = []
now = datetime.datetime.now()
STARTTIME = now.strftime("%Y-%m-%d_%H:%M:%S")
topic = "sectorq/amd/restore"
msg = {"mode":"restore", "status":"restore","bak_name":"s","host":0,"cur_job":"aaa","start_time":1,"end_time":1,"progress":0,"finished":0,"used_space":0}
client2.publish(topic, json.dumps(msg),qos=0, retain=True)
#client2.publish(topic, msg)
topic = "sectorq/amd/backups"
msg = {"mode":_MODE, "status":"started","bak_name":"complete","host":"","cur_job":"","start_time":STARTTIME,"end_time":"in progress","progress":0,"finished":",".join(finished)}
client2.publish(topic, json.dumps(msg),qos=0, retain=True)
# iterate over files in
# that directory
host = server
logging.info("Backup")
for b in backups[host]["jobs"]:
if not backups[host]["jobs"][b]["active"]:
logging.info("Backup {} is not active!".format(b))
msg = {"status":"inactive","bak_name":b,"start_time":"inactive","end_time":"inactive","progress":0}
client2.publish(topic, json.dumps(msg),qos=0, retain=True)
continue
SOURCE_DIR = backups[host]["jobs"][b]["source"]
now = datetime.datetime.now()
BACKUP_HOST = backups[host]["login"]
BACKUP_DEVICE = "/mnt/raid"
BACKUP_DIR = f"{BACKUP_HOST}:{SOURCE_DIR}"
BACKUP_ROOT = f"{BACKUP_DEVICE}/backup/{host}/{b}"
DATETIME = now.strftime("%Y-%m-%d_%H-%M-%S")
if _FIRST:
NEW_BACKUP_DIR = f"{BACKUP_ROOT}/initial"
else:
NEW_BACKUP_DIR = f"{BACKUP_ROOT}/{DATETIME}_running"
FULL_BACKUP_LATEST = f"{BACKUP_ROOT}/latest"
# msg = {"status":"started","bak_name":b,"start_time":DATETIME,"end_time":"in progress", "progress":0}
msg = {"mode":_MODE, "status":"started","bak_name":"complete","host":host,"cur_job":b,"start_time":STARTTIME,"end_time":"in progress","progress":0,"finished":",".join(finished)}
client2.publish(topic, json.dumps(msg),qos=0, retain=True)
cmnd = "mkdir -p " + NEW_BACKUP_DIR
logging.info(cmnd)
if _EXECUTE:
status, output = subprocess.getstatusoutput(cmnd)
logging.info(output)
logging.info(status)
logging.info("Create backup dir")
cmnd = f"ssh {BACKUP_HOST} 'ls {SOURCE_DIR}'"
logger.debug(cmnd)
status, output = subprocess.getstatusoutput(cmnd)
logger.debug(output)
apps = output.splitlines()
c = len(apps)
print(apps)
print(len(apps))
step = round(100 / c,1)
progress = 0
cmd = ['rsync', '-avz', '--delete', BACKUP_DIR, '--link-dest', FULL_BACKUP_LATEST, '--exclude-from=/myapps/exclude.txt', NEW_BACKUP_DIR]
logging.info(" ".join(cmd))
topic = "sectorq/amd/backups"
if not _DRYRUN:
process = subprocess.Popen(cmd,
stdout=subprocess.PIPE)
while process.poll() is None:
line = process.stdout.readline().decode("utf-8").split("/")
#print(line[0])
if line[0] in apps:
logging.info(f"Working on app {line[0]}")
while True:
if line[0] != apps[0]:
del apps[0]
progress = progress + step
else:
break
apps.remove(line[0])
sub_finished.append(line[0])
msg = {"mode":_MODE, "status":"started","bak_name":"complete","host":host,"cur_job":b,"sub":line[0],"start_time":STARTTIME,"end_time":"in progress","progress":str(round(progress)) + "%","finished":",".join(finished),"sub_finished":",".join(sub_finished)}
logging.info(f"Sending message with topic {topic} {json.dumps(msg)}")
if not "gitea-runner" == line[0]:
client2.publish(topic, json.dumps(msg),qos=0, retain=False)
progress = progress + step
topic = "sectorq/amd/restore"
for s in servers:
#if s != "rack.home.lan":
if s == "m-server.home.lan":
continue
elif s == "nas.home.lan":
user = "admin"
cmnd = "/share/Data/__GITLAB/omv_backup/venv/bin/python3 /share/Data/__GITLAB/omv_backup/omv_backup.py -r all"
cmnd = f"rm -rf {FULL_BACKUP_LATEST}"
#logging.info(cmnd)
logging.info("Removing latest link")
# input("????")
if not _DRYRUN:
status, output = subprocess.getstatusoutput(cmnd)
if _FIRST:
cmnd = f"cd {BACKUP_ROOT}; ln -s initial latest"
else:
cmnd = f"cd {BACKUP_ROOT}; mv {DATETIME}_running {DATETIME};ln -s {DATETIME} latest"
logging.info("Creating new latest link")
#print(cmnd)
# input("????")
if not _DRYRUN:
status, output = subprocess.getstatusoutput(cmnd)
#Remove old
logging.info("Removing old dirs")
cmnd = f"ls {BACKUP_ROOT}"
if not _DRYRUN:
status, output = subprocess.getstatusoutput(cmnd)
for f in output.splitlines():
pattern = r"^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}$" # regex pattern: string starts with 'abc'
# logging.info(f"Checking {f}")
if re.match(pattern, f):
# logging.info("Match!")
dt = datetime.datetime.strptime(f, "%Y-%m-%d_%H-%M-%S")
epoch_time = int(dt.timestamp())
now_epoch = int(datetime.datetime.now().timestamp())
x = now_epoch - epoch_time
# logging.info(epoch_time) # Output: 45
if x > 2592000:
dir_path = f"{BACKUP_ROOT}/{f}"
logging.info(f"removing {dir_path}")
shutil.rmtree(dir_path)
else:
user = "jd"
cmnd = "sudo /myapps/omv_backup.py -r all"
msg = {"mode":_MODE, "status":"restore","bak_name":"s","host":s,"cur_job":"aaa","start_time":1,"end_time":1,"progress":0,"finished":1,"used_space":1}
logging.info(msg)
print("No match.")
if not _DRYRUN:
logging.info(f"Clearing multiple days")
multiple_per_day = {}
to_remove = []
for f in output.splitlines():
pattern = r"^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}$" # regex pattern: string starts with 'abc'
send_mqtt_message(msg)
if is_port_open(s,22):
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
# Add SSH host key automatically if needed.
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to router using username/password authentication.
logger.info(f"Sync {s}")
print(f"Sync {s}")
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
pkey = paramiko.RSAKey.from_private_key_file("/home/jd/.ssh/id_rsa")
ssh.connect(s,
username=user,
look_for_keys=False,
allow_agent=False,
pkey=pkey)
print(cmnd)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmnd)
for line in iter(ssh_stdout.readline, ""):
logger.info(line)
print(line, end="")
for line in iter(ssh_stderr.readline, ""):
logger.info(line)
ssh.close()
try:
os.remove("/backups/restore")
except:
pass
# if _MODE == "auto":
# hostup = True
# cmnd = "ssh root@omv.home.lan 'systemctl suspend &'"
# status, output = subprocess.getstatusoutput(cmnd)
# while hostup:
# #HOST_UP = os.system(f"ping -c 1 -w 2 omv.home.lan") == 0
# cmnd = f"ping -c 1 -w 2 {BACKUP_HOST}"
# status, output = subprocess.getstatusoutput(cmnd)
# # print(status)
# # print(output)
# if status == 0:
# print(f"Backup host up, waiting - {n}\r", end="")
# time.sleep(5)
# n += 1
# else:
# print("Backup host down " )
# hostup = False
# try:
# url = "http://m-server.home.lan:8123/api/webhook/-0eWYFhSTzdusAO8jwQS9t1AT?mode=off"
if re.match(pattern, f):
cday = f.split("_")[0]
if cday in multiple_per_day:
multiple_per_day[cday].append(f)
else:
multiple_per_day[cday] = [f]
# x = requests.post(url)
# print(x.text)
# except:
# pass
# # logging.info("Match!")
# dt = datetime.datetime.strptime(f, "%Y-%m-%d_%H-%M-%S")
# epoch_time = int(dt.timestamp())
# now_epoch = int(datetime.datetime.now().timestamp())
# x = now_epoch - epoch_time
# # logging.info(epoch_time) # Output: 45
# if x > 2592000:
# dir_path = f"{BACKUP_ROOT}/{f}"
# logging.info(f"removing {dir_path}")
# shutil.rmtree(dir_path)
else:
print("No match.")
logging.info(f"Clearing multiple days: {multiple_per_day}")
for f in multiple_per_day:
logging.info(f"Looping multiple_per_day : {f}")
if len(multiple_per_day[f]) > 1:
last = multiple_per_day[f][-1]
multiple_per_day[f].pop()
logging.info(f"Last from day: {last}")
for d in multiple_per_day[f]:
logging.info(f"Looping multiple_per_day : {f} : {d}")
dir_path = f"{BACKUP_ROOT}/{d}"
logging.info(f"removing {dir_path}")
shutil.rmtree(dir_path)
cmnd = f"ls {BACKUP_ROOT}|grep _running"
logging.info(f"removing obsolete dirs")
if not _DRYRUN:
status, output = subprocess.getstatusoutput(cmnd)
for f in output.splitlines():
dir_path = f"{BACKUP_ROOT}/{f}"
logging.info(f"removing {dir_path}")
shutil.rmtree(dir_path)
now = datetime.datetime.now()
ENDTIME = now.strftime("%Y-%m-%d_%H:%M:%S")
#msg = {"status":"finished","bak_name":b,"start_time":DATETIME,"end_time":ENDTIME,"progress":0}
finished.append(b)
msg = {"mode":_MODE, "status":"finished","bak_name":"complete","host":host,"cur_job":b,"start_time":ENDTIME,"end_time":"in progress","progress":0,"finished":",".join(finished)}
client2.publish(topic, json.dumps(msg),qos=0, retain=True)
logging.info("Getting size of FS")
cmnd = "df -h /mnt/raid|awk '{ print $3 }'|tail -1"
logging.info(cmnd)
status, output = subprocess.getstatusoutput(cmnd)
used_space = (output.split())[0]
now = datetime.datetime.now()
ENDJOB = now.strftime("%Y-%m-%d_%H:%M:%S")
logging.info("Size : {}".format(used_space))
logging.info("Sending finished status")
#msg = {"mode":_MODE,"status":"finished","bak_name":"complete","start_time":STARTTIME,"end_time":ENDJOB,"progress":0,"used_space":used_space}
msg = {"mode":_MODE, "status":"finished","bak_name":"complete","host":host,"cur_job":b,"start_time":STARTTIME,"end_time":ENDTIME,"progress":0,"finished":",".join(finished),"used_space":used_space}
logging.info(msg)
client2.publish(topic, json.dumps(msg),qos=0, retain=True)
topic = "sectorq/backups/start"
logging.info(f"LALA : {topic}")
client2.publish(topic, "finished",qos=0, retain=True)
time.sleep(1)
client2.publish(topic, "finished2",qos=0, retain=True)
client2.disconnect()
#return "finished"
if _DRYRUN:
return
topic = "sectorq/amd/restore"
for s in servers:
logging.info(f"Restoring {s}")
#if s != "rack.home.lan":
if s == "m-server.home.lan":
continue
elif s == "nas.home.lan":
user = "admin"
cmnd = "/share/Data/python/bin/python3 /share/Data/__GITLAB/omv_backup/omv_backup.py -r all"
else:
user = "jd"
cmnd = "sudo /myapps/omv_backup.py -r all"
msg = {"mode":_MODE, "status":"restore","bak_name":"s","host":s,"cur_job":"aaa","start_time":1,"end_time":1,"progress":0,"finished":1,"used_space":1}
#logging.info(msg)
send_mqtt_message(topic,msg)
#continue
if is_port_open(s,22):
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
# Add SSH host key automatically if needed.
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to router using username/password authentication.
logger.info(f"Sync {s}")
print(f"Sync {s}")
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
pkey = paramiko.RSAKey.from_private_key_file("/home/jd/.ssh/id_rsa")
ssh.connect(s,
username=user,
look_for_keys=False,
allow_agent=False,
pkey=pkey)
print(cmnd)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmnd)
for line in iter(ssh_stdout.readline, ""):
logger.info(line)
print(line, end="")
for line in iter(ssh_stderr.readline, ""):
logger.info(line)
ssh.close()
try:
os.remove("/backups/restore")
except:
pass
return "finished1"
if _RESTORE:
restore_job(_APP)
sys.exit()
if _SSH_TEST:
user = "root"
cmnd = "ls -la"
@@ -724,9 +786,9 @@ if _SSH_TEST:
# user = "admin"
# cmnd = "/share/Data/__GITLAB/omv_backup/venv/bin/python3 /share/Data/__GITLAB/omv_backup/omv_backup.py -r all"
msg = {"mode":_MODE, "status":"restore","bak_name":"s","host":s,"cur_job":"aaa","start_time":1,"end_time":1,"progress":0,"finished":1,"used_space":1}
logging.info(msg)
#logging.info(msg)
send_mqtt_message(msg)
send_mqtt_message(topic,msg)
if s != "rack.home.lan":
continue
if is_port_open(s,22):
@@ -751,4 +813,57 @@ if _SSH_TEST:
print(line, end="")
for line in iter(ssh_stderr.readline, ""):
logger.info(line)
ssh.close()
ssh.close()
# Define actions based on payload
def handle_payload(payload):
try:
pl = json.loads(payload)
except:
pl = payload
logging.debug(pl)
if "host" in pl:
if pl["host"] == 'm-server':
logging.info("💡 Starting backup job")
backup_job(pl)
logging.info(f"💡 Finished backup job")
elif pl["host"] == 'nas':
logging.info("💡 Starting backup job")
backup_job(pl)
logging.info(f"💡 Finished backup job")
else:
logging.error(f"⚠️ Unknown command: {pl}")
else:
logging.error(f"⚠️ Wrong payload: {pl}")
# Callback when connected
def on_connect(client, userdata, flags, rc):
if rc == 0:
logging.info("✅ Connected securely to broker")
client.subscribe(TOPIC)
logging.info(f"📡 Subscribed to topic: {TOPIC}")
else:
logging.error(f"❌ Connection failed with code {rc}")
# Callback when a message is received
def on_message(client, userdata, msg):
payload = msg.payload.decode()
logging.info(f"📨 Received message: {payload} on topic: {msg.topic}")
handle_payload(payload)
# MQTT client setup
client = mqtt.Client()
client.username_pw_set(USERNAME, PASSWORD)
client.on_connect = on_connect
client.on_message = on_message
# Use TLS for encrypted connection
if USE_TLS:
client.tls_set() # You can customize this with certs if needed
# Connect and loop forever
client.connect(BROKER, PORT, keepalive=60)
client.publish("sectorq/backups/start", "finished", qos=0, retain=True)
client.loop_forever()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.