Skip to content

Commit

Permalink
auto_backup: allow to change the format of backup (OCA#1333)
Browse files Browse the repository at this point in the history
auto_backup: use rec.backup_format in cleanup function
  • Loading branch information
Rodrigo Macedo authored and Jordi Riera committed Jan 3, 2019
1 parent 395c062 commit 57d202c
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 38 deletions.
2 changes: 1 addition & 1 deletion auto_backup/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{
"name": "Database Auto-Backup",
"summary": "Backups database",
"version": "11.0.1.1.0",
"version": "11.0.1.0.0",
"author": (
"Yenthe Van Ginneken, "
"Agile Business Group, "
Expand Down
32 changes: 26 additions & 6 deletions auto_backup/i18n/auto_backup.pot
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ msgstr ""
msgid "Backup Failed"
msgstr ""

#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_backup_format
msgid "Backup Format"
msgstr ""

#. module: auto_backup
#: model:ir.actions.server,name:auto_backup.ir_cron_backup_scheduler_0_ir_actions_server
#: model:ir.cron,cron_name:auto_backup.ir_cron_backup_scheduler_0
Expand Down Expand Up @@ -66,25 +71,30 @@ msgstr ""
msgid "Cannot duplicate a configuration."
msgstr ""

#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_backup_format
msgid "Choose the format for this backup."
msgstr ""

#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_method
msgid "Choose the storage method for this backup."
msgstr ""

#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:249
#: code:addons/auto_backup/models/db_backup.py:265
#, python-format
msgid "Cleanup of old database backups failed."
msgstr ""

#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:128
#: code:addons/auto_backup/models/db_backup.py:137
#, python-format
msgid "Connection Test Failed!"
msgstr ""

#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:123
#: code:addons/auto_backup/models/db_backup.py:132
#, python-format
msgid "Connection Test Succeeded!"
msgstr ""
Expand All @@ -105,14 +115,14 @@ msgid "Database Backup"
msgstr ""

#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:203
#: code:addons/auto_backup/models/db_backup.py:219
#: model:mail.message.subtype,description:auto_backup.mail_message_subtype_failure
#, python-format
msgid "Database backup failed."
msgstr ""

#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:211
#: code:addons/auto_backup/models/db_backup.py:227
#: model:mail.message.subtype,description:auto_backup.mail_message_subtype_success
#, python-format
msgid "Database backup succeeded."
Expand All @@ -129,7 +139,7 @@ msgid "Display Name"
msgstr ""

#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:114
#: code:addons/auto_backup/models/db_backup.py:123
#, python-format
msgid "Do not save backups on your filestore, or you will backup your backups too!"
msgstr ""
Expand Down Expand Up @@ -294,8 +304,18 @@ msgstr ""
msgid "john"
msgstr ""

#. module: auto_backup
#: selection:db.backup,backup_format:0
msgid "pg_dump custom format (without filestore)"
msgstr ""

#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_conf_form
msgid "sftp.example.com"
msgstr ""

#. module: auto_backup
#: selection:db.backup,backup_format:0
msgid "zip (includes filestore)"
msgstr ""

58 changes: 42 additions & 16 deletions auto_backup/models/db_backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ class DbBackup(models.Model):
"read permissions for that file.",
)

backup_format = fields.Selection(
[
("zip", "zip (includes filestore)"),
("dump", "pg_dump custom format (without filestore)")
],
default='zip',
help="Choose the format for this backup."
)

frequency = fields.Selection(
(["daily", "Daily"], ["hourly", "Hourly"]),
default="daily",
Expand Down Expand Up @@ -138,11 +147,11 @@ def action_sftp_test_connection(self):
def action_backup(self):
"""Run selected backups."""
backup = None
filename = self.filename(datetime.now())
successful = self.browse()

# Start with local storage
for rec in self.filtered(lambda r: r.method == "local"):
filename = self.filename(datetime.now(), ext=rec.backup_format)
with rec.backup_log():
# Directory must exist
try:
Expand All @@ -158,21 +167,28 @@ def action_backup(self):
shutil.copyfileobj(cached, destiny)
# Generate new backup
else:
db.dump_db(self.env.cr.dbname, destiny)
db.dump_db(
self.env.cr.dbname,
destiny,
backup_format=rec.backup_format
)
backup = backup or destiny.name
successful |= rec

# Ensure a local backup exists if we are going to write it remotely
sftp = self.filtered(lambda r: r.method == "sftp")
if sftp:
if backup:
cached = open(backup)
else:
cached = db.dump_db(self.env.cr.dbname, None)

with cached:
for rec in sftp:
with rec.backup_log():
for rec in sftp:
filename = self.filename(datetime.now(), ext=rec.backup_format)
with rec.backup_log():

cached = db.dump_db(
self.env.cr.dbname,
None,
backup_format=rec.backup_format
)

with cached:
with rec.sftp_connection() as remote:
# Directory must exist
try:
Expand Down Expand Up @@ -227,18 +243,25 @@ def cleanup(self):
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))
oldest = self.filename(
now - timedelta(days=rec.days_to_keep),
ext=rec.backup_format
)

if rec.method == "local":
for name in iglob(os.path.join(rec.folder,
"*.dump.zip")):
path = os.path.join(
rec.folder,
"*.{}".format(rec.backup_format)
)

for name in iglob(path):
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
if (name.endswith(rec.backup_format) and
os.path.basename(name) < oldest):
remote.unlink('%s/%s' % (rec.folder, name))

Expand Down Expand Up @@ -266,13 +289,16 @@ def cleanup_log(self):
self.name)

@staticmethod
def filename(when):
def filename(when, ext='zip'):
"""Generate a file name for a backup.
:param datetime.datetime when:
Use this datetime instead of :meth:`datetime.datetime.now`.
:param str ext: Extension of the file. Default: dump.zip
"""
return "{:%Y_%m_%d_%H_%M_%S}.dump.zip".format(when)
return "{:%Y_%m_%d_%H_%M_%S}.{ext}".format(
when, ext='dump.zip' if ext == 'zip' else ext
)

@api.multi
def sftp_connection(self):
Expand Down
29 changes: 14 additions & 15 deletions auto_backup/tests/test_db_backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,6 @@ def test_action_backup_sftp_remote_open(self):
'wb'
)

def test_action_backup_sftp_remote_open(self):
""" It should open remote file w/ proper args """
rec_id = self.new_record()
with self.mock_assets() as assets:
with self.patch_filtered_sftp(rec_id):
conn = rec_id.sftp_connection().__enter__()
rec_id.action_backup()
conn.open.assert_called_once_with(
assets['os'].path.join(),
'wb'
)

def test_action_backup_daily(self):
""" It should search all records with daily frequency"""
rec_id = self.new_record()
Expand All @@ -203,7 +191,7 @@ def test_action_backup_hourly(self):
with mock.patch.object(rec_id, 'search'):
rec_id.action_backup_all("hourly")
rec_id.search.assert_called_once_with(
[("frequency", "=", "daily")]
[("frequency", "=", "hourly")]
)

def test_action_backup_all_return(self):
Expand Down Expand Up @@ -253,12 +241,24 @@ def test_sftp_connection_return(self, pysftp):
pysftp.Connection(), res,
)

def test_filename(self):
def test_filename_default(self):
""" It should not error and should return a .dump.zip file str """
now = datetime.now()
res = self.Model.filename(now)
self.assertTrue(res.endswith(".dump.zip"))

def test_filename_zip(self):
""" It should return a dump.zip filename"""
now = datetime.now()
res = self.Model.filename(now, ext='zip')
self.assertTrue(res.endswith(".dump.zip"))

def test_filename_dump(self):
""" It should return a dump filename"""
now = datetime.now()
res = self.Model.filename(now, ext='dump')
self.assertTrue(res.endswith(".dump"))

def test_default_frequence_daily(self):
""" The default frequency is daily"""
rec_id = self.new_record()
Expand All @@ -275,4 +275,3 @@ def test_frequency_hourly(self):
'hourly',
rec_id.frequency
)

1 change: 1 addition & 0 deletions auto_backup/view/db_backup_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<field name="days_to_keep"/>
<field name="frequency"/>
<field name="method"/>
<field name="backup_format"/>
</group>
<div attrs="{'invisible': [('method', '!=', 'sftp')]}">
<div class="bg-warning text-warning">
Expand Down

0 comments on commit 57d202c

Please sign in to comment.