could work as is.

This commit is contained in:
2023-11-12 16:42:24 +01:00
parent e248b359fa
commit 78e41be752
2 changed files with 73 additions and 110 deletions

View File

@@ -8,3 +8,4 @@ sshfolder=path to the .ssh folder to use
remotehost= remotehost=
repository= repository=
passwordfile= passwordfile=
dryrun=

170
main.py
View File

@@ -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()
@@ -204,10 +194,16 @@ def backup_services(servicename: str, servicesdict: dict, composefilepath: str)
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]:
@@ -219,47 +215,13 @@ def backup_services(servicename: str, servicesdict: dict, composefilepath: str)
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)