%s
%s" % ( + _("Database backup failed."), + escaped_tb), + subtype=self.env.ref("auto_backup.failure")) + else: + _logger.info("Database backup succeeded: %s", self.name) + self.message_post(_("Database backup succeeded.")) + + @api.multi + def cleanup(self): + """Clean up old backups.""" + now = datetime.now() + for rec in self.filtered("days_to_keep"): + with rec.cleanup_log(): + oldest = self.filename(now - timedelta(days=rec.days_to_keep)) + + if rec.method == "local": + for name in iglob(os.path.join(rec.folder, + "*.dump.zip")): + if os.path.basename(name) < oldest: + os.unlink(name) + + elif rec.method == "sftp": + with rec.sftp_connection() as remote: + for name in remote.listdir(rec.folder): + if (name.endswith(".dump.zip") and + os.path.basename(name) < oldest): + remote.unlink(name) + + @api.multi + @contextmanager + def cleanup_log(self): + """Log a possible cleanup failure.""" + self.ensure_one() + try: + _logger.info("Starting cleanup process after database backup: %s", + self.name) + yield + except: + _logger.exception("Cleanup of old database backups failed: %s") + escaped_tb = tools.html_escape(traceback.format_exc()) + self.message_post( + "
%s
%s" % ( + _("Cleanup of old database backups failed."), + escaped_tb), + subtype=self.env.ref("auto_backup.failure")) + else: + _logger.info("Cleanup of old database backups succeeded: %s", + self.name) + + @api.model + def filename(self, when): + """Generate a file name for a backup. + + :param datetime.datetime when: + Use this datetime instead of :meth:`datetime.datetime.now`. + """ + return "{:%Y_%m_%d_%H_%M_%S}.dump.zip".format(when) + + @api.multi + def sftp_connection(self): + """Return a new SFTP connection with found parameters.""" + self.ensure_one() + params = { + "host": self.sftp_host, + "username": self.sftp_user, + "port": self.sftp_port, + } + _logger.debug( + "Trying to connect to sftp://%(username)s@%(host)s:%(port)d", + extra=params) + if self.sftp_private_key: + params["private_key"] = self.sftp_private_key + if self.sftp_password: + params["private_key_pass"] = self.sftp_password + else: + params["password"] = self.sftp_password + + return pysftp.Connection(**params) diff --git a/auto_backup/security/ir.model.access.csv b/auto_backup/security/ir.model.access.csv new file mode 100644 index 000000000..ded4108f4 --- /dev/null +++ b/auto_backup/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_db_backup_read,Read db.backup,model_db_backup,base.group_erp_manager,1,0,0,0 +access_db_backup_write,Write db.backup,model_db_backup,base.group_system,1,1,1,1 diff --git a/auto_backup/static/description/icon.png b/auto_backup/static/description/icon.png new file mode 100644 index 000000000..24c59d3f5 Binary files /dev/null and b/auto_backup/static/description/icon.png differ diff --git a/auto_backup/static/description/icon.svg b/auto_backup/static/description/icon.svg new file mode 100644 index 000000000..b28b182e1 --- /dev/null +++ b/auto_backup/static/description/icon.svg @@ -0,0 +1,51 @@ + + diff --git a/auto_backup/tests/__init__.py b/auto_backup/tests/__init__.py new file mode 100644 index 000000000..e803c71af --- /dev/null +++ b/auto_backup/tests/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# © 2015 Agile Business Group