could work as is.
This commit is contained in:
@@ -8,3 +8,4 @@ sshfolder=path to the .ssh folder to use
|
|||||||
remotehost=
|
remotehost=
|
||||||
repository=
|
repository=
|
||||||
passwordfile=
|
passwordfile=
|
||||||
|
dryrun=
|
||||||
174
main.py
174
main.py
@@ -17,7 +17,7 @@ passwordFile = None
|
|||||||
def arguments_parser():
|
def arguments_parser():
|
||||||
'''
|
'''
|
||||||
récupère les arguments de la commande
|
récupère les arguments de la commande
|
||||||
:return:
|
:return: object with arguments as members
|
||||||
'''
|
'''
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog='BackandUp',
|
prog='BackandUp',
|
||||||
@@ -38,78 +38,67 @@ def arguments_parser():
|
|||||||
help='Patterns of volume or bind to include from the backup. Using this argument, '
|
help='Patterns of volume or bind to include from the backup. Using this argument, '
|
||||||
'only matching volumes and binds will be backed up.')
|
'only matching volumes and binds will be backed up.')
|
||||||
|
|
||||||
args = parser.parse_args()
|
return parser.parse_args()
|
||||||
bindincludepattern = args.bindinclude
|
|
||||||
bindexcludepattern = args.bindexclude
|
|
||||||
configfile = args.config
|
|
||||||
composefile = args.compose
|
|
||||||
sshfolder = args.sshfolder
|
|
||||||
remotehost = None
|
|
||||||
repository = None
|
|
||||||
passwordfile = None
|
|
||||||
|
|
||||||
|
|
||||||
def read_config_file(filepath: str = "configuration.ini"):
|
def read_config_file(filepath: str = "configuration.ini"):
|
||||||
parser = configparser.ConfigParser()
|
parser = configparser.ConfigParser()
|
||||||
parser.read(filepath)
|
parser.read(filepath)
|
||||||
global bindingIncludePattern
|
global arguments
|
||||||
global bindingExcludePattern
|
|
||||||
global remoteHost
|
|
||||||
global repository
|
|
||||||
global passwordFile
|
|
||||||
bindingIncludePattern = parser["backandup"]["bindincludepattern"]
|
|
||||||
bindingExcludePattern = parser["backandup"]["bindexcludepattern"]
|
|
||||||
remoteHost = parser["restic"]["remotehost"]
|
|
||||||
remoteUser = parser["restic"]["remoteuser"]
|
|
||||||
repository = parser["restic"]["repository"]
|
|
||||||
passwordFile = parser["restic"]["passwordfile"]
|
|
||||||
|
|
||||||
|
if "backandup" in parser:
|
||||||
|
if "composefile" in parser["backandup"]:
|
||||||
|
arguments.compose = parser["backandup"]["composefile"]
|
||||||
|
if "composefile" in parser["backandup"]:
|
||||||
|
arguments.sshfolder = parser["backandup"]["sshfolder"]
|
||||||
|
if "composefile" in parser["backandup"]:
|
||||||
|
arguments.bindinclude = parser["backandup"]["bindincludepattern"]
|
||||||
|
if "composefile" in parser["backandup"]:
|
||||||
|
arguments.bindexclude = parser["backandup"]["bindexcludepattern"]
|
||||||
|
|
||||||
def do_backup(dirpath: str, tags: str = None):
|
if "restic" in parser:
|
||||||
restic.repository = f"sftp:{remoteUser}@{remoteHost}:/{repository}"
|
arguments.remotehost = parser["restic"]["remotehost"]
|
||||||
restic.password_file = passwordFile
|
arguments.repository = parser["restic"]["repository"]
|
||||||
if tags is None:
|
arguments.passwordfile = parser["restic"]["passwordfile"]
|
||||||
restic.backup(paths=[dirpath])
|
if "dryrun" in parser["restic"]:
|
||||||
|
arguments.dryrun = (parser["restic"]["dryrun"].lower() == "true" or parser["restic"]["dryrun"] > 0)
|
||||||
else:
|
else:
|
||||||
restic.backup(paths=[dirpath], tags=[])
|
arguments.dryrun = False
|
||||||
|
|
||||||
|
|
||||||
def backup_ct_binds(ct: docker.models.containers.Container, includepattern: str | list = None,
|
def restic_backup(dirpath: str, tagslist: list = None):
|
||||||
excludepattern: str | list = None):
|
'''
|
||||||
if ct.attrs['HostConfig']['Binds'] is None:
|
Lance la sauvegarde avec un container restic one-shot
|
||||||
print(" Nothing to backup")
|
:param dirpath: str
|
||||||
return 0
|
:param tags: list
|
||||||
if type(includepattern) is str:
|
:return:
|
||||||
includepattern = [includepattern]
|
'''
|
||||||
if type(excludepattern) is str:
|
dockerhost = docker.from_env()
|
||||||
excludepattern = [excludepattern]
|
ctpath = dirpath
|
||||||
if includepattern is None:
|
# si il s'agit d'un volume on ne peut pas deviner son point de montage alors on le met arbitrairement à la racine
|
||||||
includepattern = ["NOPATTERNTOINCLUDEXXXXXX"]
|
if not os.path.exists(dirpath):
|
||||||
if excludepattern is None:
|
ctpath = f"/{ctpath}"
|
||||||
excludepattern = ["NOPATTERNTOEXCLUDEXXXXXX"]
|
|
||||||
for BindMount in ct.attrs['HostConfig']['Binds']:
|
|
||||||
BindPath = BindMount.split(":")[0]
|
|
||||||
print(f" {BindPath}")
|
|
||||||
# Si on matche au moins 1 pattern d'inclusion ET aucun pattern d'exclusion
|
|
||||||
if any(x in BindPath for x in includepattern) and not any(x in BindPath for x in excludepattern):
|
|
||||||
do_backup(BindPath)
|
|
||||||
else:
|
|
||||||
print(" not a directory to backup")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
tagscommand = ""
|
||||||
|
if tagslist is not None and len(tagslist):
|
||||||
|
for tag in tagslist:
|
||||||
|
tagscommand = f"{tagscommand}--tag {tag} "
|
||||||
|
tagscommand = tagscommand[:-1]
|
||||||
|
|
||||||
def stop_backup_restart_container(ct: docker.models.containers.Container):
|
dryrun = ""
|
||||||
print(ct.name)
|
if arguments.dryrun:
|
||||||
dorestart = False
|
dryrun = "--dry-run"
|
||||||
if ct.attrs['State']['Status'] == 'running':
|
|
||||||
print(" stop the container")
|
volumes = {dirpath: {'bind': dirpath, 'mode': 'ro'},
|
||||||
dorestart = True
|
arguments.sshfolder: {'bind': '/root/.ssh', 'mode': 'ro'},
|
||||||
ct.stop()
|
arguments.passwordfile: {'bind': "/repopassword", 'mode': 'ro'}}
|
||||||
backup_ct_binds(ct, bindingIncludePattern, bindingExcludePattern)
|
command = f"backup {dryrun} {tagscommand} {dirpath}"
|
||||||
if dorestart:
|
hostname = "restic"
|
||||||
print(" start the container")
|
environment = ["RESTIC_PASSWORD_FILE=/repopassword",
|
||||||
ct.start()
|
f"RESTIC_REPOSITORY={arguments.repository}"]
|
||||||
return 0
|
|
||||||
|
dockerhost.containers.run("restic/restic", command=command, environment=environment,
|
||||||
|
volumes=volumes, remove=True, hostname=hostname)
|
||||||
|
|
||||||
|
|
||||||
def yaml_to_services_list(filepath: str) -> dict|None:
|
def yaml_to_services_list(filepath: str) -> dict|None:
|
||||||
@@ -188,9 +177,10 @@ def backup_services(servicename: str, servicesdict: dict, composefilepath: str)
|
|||||||
:param composefilepath: str
|
:param composefilepath: str
|
||||||
:return: bool
|
:return: bool
|
||||||
'''
|
'''
|
||||||
|
print(f"Starting process for {servicename}:")
|
||||||
|
|
||||||
if servicesdict[servicename]["backupdone"]:
|
if servicesdict[servicename]["backupdone"]:
|
||||||
print(f"backup is already done for {servicename}, ignoring")
|
print(f" backup is already done, ignoring")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
dockerclient = docker.from_env()
|
dockerclient = docker.from_env()
|
||||||
@@ -199,67 +189,39 @@ def backup_services(servicename: str, servicesdict: dict, composefilepath: str)
|
|||||||
for containerid in containersid:
|
for containerid in containersid:
|
||||||
container = dockerclient.containers.get(containerid)
|
container = dockerclient.containers.get(containerid)
|
||||||
containerrunning.append(container.status == "running")
|
containerrunning.append(container.status == "running")
|
||||||
print(f"stopping {container.name}")
|
print(f" stopping {container.name}")
|
||||||
container.stop()
|
container.stop()
|
||||||
|
|
||||||
if len(servicesdict[servicename]["must_before"]):
|
if len(servicesdict[servicename]["must_before"]):
|
||||||
for dependencie in servicesdict[servicename]["must_before"]:
|
for dependencie in servicesdict[servicename]["must_before"]:
|
||||||
backup_service(dependencie, servicesdict, composefilepath)
|
backup_services(dependencie, servicesdict, composefilepath)
|
||||||
|
|
||||||
# TODO: faire la sauvegarde
|
# TODO: faire la sauvegarde
|
||||||
print(f"doing backup of {servicename}")
|
for containerid in containersid:
|
||||||
|
container = dockerclient.containers.get(containerid)
|
||||||
|
if "Binds" in container.attrs["HostConfig"] and len(container.attrs["HostConfig"]["Binds"]):
|
||||||
|
for bind in container.attrs["HostConfig"]["Binds"]:
|
||||||
|
bindpath = bind.split(":")[0]
|
||||||
|
print(f" backup of {bindpath}")
|
||||||
|
restic_backup(dirpath=bindpath, tagslist=[servicename])
|
||||||
|
|
||||||
for containerindex in range(0, len(containersid)):
|
for containerindex in range(0, len(containersid)):
|
||||||
if containerrunning[containerindex]:
|
if containerrunning[containerindex]:
|
||||||
container = dockerclient.containers.get(containersid[containerindex])
|
container = dockerclient.containers.get(containersid[containerindex])
|
||||||
print(f"restarting {container.name}")
|
print(f" restarting {container.name}")
|
||||||
container.start()
|
container.start()
|
||||||
|
|
||||||
servicesdict[servicename]["backupdone"] = True
|
servicesdict[servicename]["backupdone"] = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def volume_backup(itemtobackup: str) -> bool:
|
|
||||||
'''
|
|
||||||
Exécute la sauvegarde du dossier/fichier bind ou volume du container.
|
|
||||||
:param itemtobackup: str
|
|
||||||
:return: bool
|
|
||||||
'''
|
|
||||||
# il faut lancer l'équivalent de ceci :
|
|
||||||
# docker run --rm -v $(itemtobackup):/$(itemtobackup) -v $passwordfile:/$passwordfile -v $sshfolder:/root/.ssh \
|
|
||||||
# --host $host restic/restic restic --password-file /$passwordfile -r $remotehost backup /$(itemtobackup)
|
|
||||||
|
|
||||||
volumes = {sshfolder: {'bind': '/root/.ssh', 'mode': 'ro'},
|
|
||||||
passwordfile: {'bind': '/.resticpass', 'mode': 'ro'}}
|
|
||||||
ctpath = itemtobackup
|
|
||||||
if itemtobackup.find("/") == -1 :
|
|
||||||
ctpath = f"/{itemtobackup}"
|
|
||||||
|
|
||||||
dockerclient = docker.from_env()
|
|
||||||
backuplog = dockerclient.containers.run("restic/restic", command="Lol")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Press the green button in the gutter to run the script.
|
# Press the green button in the gutter to run the script.
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
dockerhost = docker.from_env()
|
dockerhost = docker.from_env()
|
||||||
|
arguments = arguments_parser()
|
||||||
|
|
||||||
bindincludepattern = None
|
servicesDict = yaml_to_services_list(arguments.compose)
|
||||||
bindexcludepattern = None
|
servicesList = order_services_by_dependencies(servicesDict)
|
||||||
configfile = None
|
print("Backup process is starting")
|
||||||
composefile = "/root/compose/core/docker-compose.yml"
|
for servicetobackup in reversed(servicesList):
|
||||||
sshfolder = "/root/.ssh"
|
backup_services(servicetobackup, servicesDict, arguments.compose)
|
||||||
remotehost = None
|
|
||||||
repository = None
|
|
||||||
passwordfile = None
|
|
||||||
|
|
||||||
arguments_parser()
|
|
||||||
|
|
||||||
# servicesDict = yaml_to_services_list(composefile)
|
|
||||||
# servicesList = order_services_by_dependencies(servicesDict)
|
|
||||||
# print("Backup process is starting")
|
|
||||||
# for servicetobackup in reversed(servicesList):
|
|
||||||
# print(f" - {servicetobackup}")
|
|
||||||
# backup_services(servicetobackup, servicesDict, composefile)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user