From 56ed214fb5e4ff039384c2e448667c52ba538aad Mon Sep 17 00:00:00 2001 From: Spitap Date: Tue, 19 Jul 2022 19:06:53 +0200 Subject: [PATCH] Starting work on backup system --- modoboa_installer/scripts/__init__.py | 7 +- modoboa_installer/scripts/backup.py | 114 ++++++++++++++++++++++++++ modoboa_installer/utils.py | 11 ++- run.py | 24 ++++-- 4 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 modoboa_installer/scripts/backup.py diff --git a/modoboa_installer/scripts/__init__.py b/modoboa_installer/scripts/__init__.py index 3edfa66..b101966 100644 --- a/modoboa_installer/scripts/__init__.py +++ b/modoboa_installer/scripts/__init__.py @@ -6,11 +6,16 @@ import sys from .. import utils -def install(appname, config, upgrade): +def install(appname, config, upgrade, backup): """Install an application.""" if (config.has_option(appname, "enabled") and not config.getboolean(appname, "enabled")): return + if backup: + utils.printcolor("Starting up backup...", utils.MAGENTA) + script = importlib.import_module("modoboa_installer.backup") + getattr(script, Backup())(config).run() + return utils.printcolor("Installing {}".format(appname), utils.MAGENTA) try: script = importlib.import_module( diff --git a/modoboa_installer/scripts/backup.py b/modoboa_installer/scripts/backup.py new file mode 100644 index 0000000..ba2b476 --- /dev/null +++ b/modoboa_installer/scripts/backup.py @@ -0,0 +1,114 @@ +"""Backup script for pre-installed instance""" + +import shutil +import utils +import os + +#TODO: have version of each modoboa componenent saved into the config file to restore the same version + +class Backup(): + + + def __init__(self, config): + self.config = config + self.destinationPath = "" + self.BACKUPDIRECTORY = ["mails", "custom", "databases"] + + + def preparePath(self): + for dir in self.BACKUPDIRECTORY: + os.mkdir(self.destinationPath + dir) + + + def validatePath(self, path): + """Check basic condition for backup directory""" + + if os.path.isfile(path): + print("Error, you provided a file instead of a directory!") + return False + + if not os.path.exists(path): + createDir = input(f"\"{path}\" doesn't exists, would you like to create it ? [Y/n]\n").lower() + + if createDir == "y" or createDir == "yes": + os.mkdir(path) + else: + return False + + if len(os.listdir(path)) != 0: + delDir = input("Warning : backup folder is not empty, it will be purged if you continue... [Y/n]").lower() + if delDir == "y" or delDir == "yes": + shutil.rmtree(path) + else: + return False + + self.destinationPath = path + + if self.destinationPath[-1] != "/": + self.destinationPath += "/" + + self.preparePath() + return True + + + def setPath(self): + """Setup backup directory""" + user_value = None + while (user_value != '' and not self.validatePath(user_value)): + print("Enter backup path, please provide an empty folder.") + user_value = utils.user_input("-> ") + + + def backupConfigFile(self): + utils.copy_file("installer.cfg", self.destinationPath) + + + def backupMails(self): + + utils.printcolor("Backing up mails", utils.MAGENTA) + + home_path = self.config.get("dovecot", "home_dir") + + if not os.path.exists(home_path) or os.path.isfile(home_path): + utils.printcolor("Error backing up Email, provided path " + f" ({home_path}) seems not right...", utils.RED) + + else: + shutil.copytree(home_path, self.destinationPath+"mails/") + utils.printcolor("Mail backup complete!", utils.GREEN) + + + def backupCustomConfig(self): + """Custom config : + - Amavis : /etc/amavis/conf.d/99-custom + - Postscreen : /etc/postfix/custom_whitelist.cidr + Feel free to suggest to add others!""" + utils.printcolor("Backing up some custom configuration...", utils.MAGENTA) + + custom_path = self.destinationPath+"custom/" + + """AMAVIS""" + amavis_custom = "/etc/amavis/conf.d/99-custom" + if os.path.isfile(amavis_custom): + utils.copy_file(amavis_custom, custom_path) + utils.printcolor("Amavis custom configuration saved!", utils.GREEN) + + """POSTSCREEN""" + postscreen_custom = "/etc/postfix/custom_whitelist.cidr" + if os.path.isfile(postscreen_custom): + utils.copy_file(postscreen_custom, custom_path) + utils.printcolor("Postscreen whitelist custom configuration saved!", utils.GREEN) + + + def backupDBs(self): + """Backing up databases""" + + + def run(self): + self.setPath() + self.backupConfigFile() + self.backupMails() + self.backupCustomConfig() + self.backupDBs() + + diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 8a793af..627f519 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -163,19 +163,26 @@ def copy_from_template(template, dest, context): fp.write(ConfigFileTemplate(buf).substitute(context)) -def check_config_file(dest, interactive=False, upgrade=False): +def check_config_file(dest, interactive=False, upgrade=False, backup=False): """Create a new installer config file if needed.""" + isPresent = True if os.path.exists(dest): - return + return isPresent if upgrade: printcolor( "You cannot upgrade an existing installation without a " "configuration file.", RED) sys.exit(1) + elif backup: + isPresent = False + printcolor( + "Your configuration file hasn't been found. A new one will be generated. " + "Please edit it with correct password for the databases !", RED) printcolor( "Configuration file {} not found, creating new one." .format(dest), YELLOW) gen_config(dest, interactive) + return isPresent def has_colours(stream): diff --git a/run.py b/run.py index 8bfeaf2..784d667 100755 --- a/run.py +++ b/run.py @@ -44,6 +44,13 @@ def upgrade_disclaimer(config): " will be impacted:", utils.BLUE ) +def backup_disclamer(): + """Display backup disclamer. """ + utils.printcolor( + "Your mail server will be backed up (messages and databases) locally." + " !! You should really transfer the backup somewhere else..." + " Custom configuration (like to postfix) won't be saved.", utils.BLUE) + def main(input_args): """Install process.""" @@ -51,6 +58,8 @@ def main(input_args): versions = ( ["latest"] + list(compatibility_matrix.COMPATIBILITY_MATRIX.keys()) ) + parser.add_argument("--backup", action="store_true", default=False, + help="Backing up previously installed instance") parser.add_argument("--debug", action="store_true", default=False, help="Enable debug output") parser.add_argument("--force", action="store_true", default=False, @@ -79,8 +88,8 @@ def main(input_args): if args.debug: utils.ENV["debug"] = True utils.printcolor("Welcome to Modoboa installer!\n", utils.GREEN) - utils.check_config_file(args.configfile, args.interactive, args.upgrade) - if args.stop_after_configfile_check: + wasConfigFileAlreadyThere = utils.check_config_file(args.configfile, args.interactive, args.upgrade, args.backup) + if args.stop_after_configfile_check or (not wasConfigFileAlreadyThere and args.backup): return config = configparser.ConfigParser() with open(args.configfile) as fp: @@ -91,11 +100,14 @@ def main(input_args): config.set("dovecot", "domain", args.domain) config.set("modoboa", "version", args.version) config.set("modoboa", "install_beta", str(args.beta)) - # Display disclaimerpython 3 linux distribution - if not args.upgrade: - installation_disclaimer(args, config) - else: + # Display disclaimer python 3 linux distribution + if args.upgrade: upgrade_disclaimer(config) + elif args.backup: + backup_disclamer() + else: + installation_disclaimer(args, config) + # Show concerned components components = [] for section in config.sections():