could work as is.
This commit is contained in:
@@ -7,4 +7,5 @@ sshfolder=path to the .ssh folder to use
|
||||
[restic]
|
||||
remotehost=
|
||||
repository=
|
||||
passwordfile=
|
||||
passwordfile=
|
||||
dryrun=
|
||||
180
main.py
180
main.py
@@ -17,7 +17,7 @@ passwordFile = None
|
||||
def arguments_parser():
|
||||
'''
|
||||
récupère les arguments de la commande
|
||||
:return:
|
||||
:return: object with arguments as members
|
||||
'''
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='BackandUp',
|
||||
@@ -38,78 +38,67 @@ def arguments_parser():
|
||||
help='Patterns of volume or bind to include from the backup. Using this argument, '
|
||||
'only matching volumes and binds will be backed up.')
|
||||
|
||||
args = parser.parse_args()
|
||||
bindincludepattern = args.bindinclude
|
||||
bindexcludepattern = args.bindexclude
|
||||
configfile = args.config
|
||||
composefile = args.compose
|
||||
sshfolder = args.sshfolder
|
||||
remotehost = None
|
||||
repository = None
|
||||
passwordfile = None
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def read_config_file(filepath: str = "configuration.ini"):
|
||||
parser = configparser.ConfigParser()
|
||||
parser.read(filepath)
|
||||
global bindingIncludePattern
|
||||
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"]
|
||||
global arguments
|
||||
|
||||
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):
|
||||
restic.repository = f"sftp:{remoteUser}@{remoteHost}:/{repository}"
|
||||
restic.password_file = passwordFile
|
||||
if tags is None:
|
||||
restic.backup(paths=[dirpath])
|
||||
else:
|
||||
restic.backup(paths=[dirpath], tags=[])
|
||||
|
||||
|
||||
def backup_ct_binds(ct: docker.models.containers.Container, includepattern: str | list = None,
|
||||
excludepattern: str | list = None):
|
||||
if ct.attrs['HostConfig']['Binds'] is None:
|
||||
print(" Nothing to backup")
|
||||
return 0
|
||||
if type(includepattern) is str:
|
||||
includepattern = [includepattern]
|
||||
if type(excludepattern) is str:
|
||||
excludepattern = [excludepattern]
|
||||
if includepattern is None:
|
||||
includepattern = ["NOPATTERNTOINCLUDEXXXXXX"]
|
||||
if excludepattern is None:
|
||||
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)
|
||||
if "restic" in parser:
|
||||
arguments.remotehost = parser["restic"]["remotehost"]
|
||||
arguments.repository = parser["restic"]["repository"]
|
||||
arguments.passwordfile = parser["restic"]["passwordfile"]
|
||||
if "dryrun" in parser["restic"]:
|
||||
arguments.dryrun = (parser["restic"]["dryrun"].lower() == "true" or parser["restic"]["dryrun"] > 0)
|
||||
else:
|
||||
print(" not a directory to backup")
|
||||
return 0
|
||||
arguments.dryrun = False
|
||||
|
||||
|
||||
def stop_backup_restart_container(ct: docker.models.containers.Container):
|
||||
print(ct.name)
|
||||
dorestart = False
|
||||
if ct.attrs['State']['Status'] == 'running':
|
||||
print(" stop the container")
|
||||
dorestart = True
|
||||
ct.stop()
|
||||
backup_ct_binds(ct, bindingIncludePattern, bindingExcludePattern)
|
||||
if dorestart:
|
||||
print(" start the container")
|
||||
ct.start()
|
||||
return 0
|
||||
def restic_backup(dirpath: str, tagslist: list = None):
|
||||
'''
|
||||
Lance la sauvegarde avec un container restic one-shot
|
||||
:param dirpath: str
|
||||
:param tags: list
|
||||
:return:
|
||||
'''
|
||||
dockerhost = docker.from_env()
|
||||
ctpath = dirpath
|
||||
# si il s'agit d'un volume on ne peut pas deviner son point de montage alors on le met arbitrairement à la racine
|
||||
if not os.path.exists(dirpath):
|
||||
ctpath = f"/{ctpath}"
|
||||
|
||||
tagscommand = ""
|
||||
if tagslist is not None and len(tagslist):
|
||||
for tag in tagslist:
|
||||
tagscommand = f"{tagscommand}--tag {tag} "
|
||||
tagscommand = tagscommand[:-1]
|
||||
|
||||
dryrun = ""
|
||||
if arguments.dryrun:
|
||||
dryrun = "--dry-run"
|
||||
|
||||
volumes = {dirpath: {'bind': dirpath, 'mode': 'ro'},
|
||||
arguments.sshfolder: {'bind': '/root/.ssh', 'mode': 'ro'},
|
||||
arguments.passwordfile: {'bind': "/repopassword", 'mode': 'ro'}}
|
||||
command = f"backup {dryrun} {tagscommand} {dirpath}"
|
||||
hostname = "restic"
|
||||
environment = ["RESTIC_PASSWORD_FILE=/repopassword",
|
||||
f"RESTIC_REPOSITORY={arguments.repository}"]
|
||||
|
||||
dockerhost.containers.run("restic/restic", command=command, environment=environment,
|
||||
volumes=volumes, remove=True, hostname=hostname)
|
||||
|
||||
|
||||
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
|
||||
:return: bool
|
||||
'''
|
||||
print(f"Starting process for {servicename}:")
|
||||
|
||||
if servicesdict[servicename]["backupdone"]:
|
||||
print(f"backup is already done for {servicename}, ignoring")
|
||||
print(f" backup is already done, ignoring")
|
||||
return True
|
||||
|
||||
dockerclient = docker.from_env()
|
||||
@@ -199,67 +189,39 @@ def backup_services(servicename: str, servicesdict: dict, composefilepath: str)
|
||||
for containerid in containersid:
|
||||
container = dockerclient.containers.get(containerid)
|
||||
containerrunning.append(container.status == "running")
|
||||
print(f"stopping {container.name}")
|
||||
print(f" stopping {container.name}")
|
||||
container.stop()
|
||||
|
||||
if len(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
|
||||
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)):
|
||||
if containerrunning[containerindex]:
|
||||
container = dockerclient.containers.get(containersid[containerindex])
|
||||
print(f"restarting {container.name}")
|
||||
print(f" restarting {container.name}")
|
||||
container.start()
|
||||
|
||||
servicesdict[servicename]["backupdone"] = 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.
|
||||
if __name__ == '__main__':
|
||||
dockerhost = docker.from_env()
|
||||
dockerhost = docker.from_env()
|
||||
arguments = arguments_parser()
|
||||
|
||||
bindincludepattern = None
|
||||
bindexcludepattern = None
|
||||
configfile = None
|
||||
composefile = "/root/compose/core/docker-compose.yml"
|
||||
sshfolder = "/root/.ssh"
|
||||
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)
|
||||
servicesDict = yaml_to_services_list(arguments.compose)
|
||||
servicesList = order_services_by_dependencies(servicesDict)
|
||||
print("Backup process is starting")
|
||||
for servicetobackup in reversed(servicesList):
|
||||
backup_services(servicetobackup, servicesDict, arguments.compose)
|
||||
|
||||
Reference in New Issue
Block a user