Browse Source

[11.0][MIG] auto_backup

pull/1384/head
Andrea 7 years ago
committed by Aitor Bouzas
parent
commit
7a32b0eddb
  1. 13
      auto_backup/README.rst
  2. 6
      auto_backup/__init__.py
  3. 12
      auto_backup/__manifest__.py
  4. 12
      auto_backup/data/ir_cron.xml
  5. 3
      auto_backup/data/mail_message_subtype.xml
  6. 5
      auto_backup/models/__init__.py
  7. 53
      auto_backup/models/db_backup.py
  8. 6
      auto_backup/tests/__init__.py
  9. 28
      auto_backup/tests/test_db_backup.py
  10. 53
      auto_backup/view/db_backup_view.xml

13
auto_backup/README.rst

@ -1,5 +1,5 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: https://www.gnu.org/licenses/agpl
:alt: License: AGPL-3
====================
@ -13,12 +13,12 @@ Installation
Before installing this module, you need to execute::
pip install pysftp
pip3 install pysftp
Configuration
=============
Go to *Settings -> Configuration -> Configure Backup* to
Go to *Settings -> Database Structure -> Automated Backup* to
create your configurations for each database that you needed
to backups.
@ -70,7 +70,7 @@ manually execute the selected processes.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/10.0
:target: https://runbot.odoo-community.org/runbot/149/11.0
Known issues / Roadmap
======================
@ -87,7 +87,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
help us smash it by providing detailed and welcomed feedback.
Credits
=======
@ -99,6 +99,7 @@ Contributors
* Alessio Gerace <alessio.gerace@agilebg.com>
* Jairo Llopis <yajo.sk8@gmail.com>
* Dave Lasley <dave@laslabs.com>
* Andrea Stirpe <a.stirpe@onestein.nl>
Maintainer
----------

6
auto_backup/__init__.py

@ -1,7 +1,3 @@
# -*- coding: utf-8 -*-
# © 2004-2009 Tiny SPRL (<http://tiny.be>).
# © 2015 Agile Business Group <http://www.agilebg.com>
# © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# License GPL-3.0 or later (http://www.gnu.org/licenses/gpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import models

12
auto_backup/__manifest__.py

@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
# © 2004-2009 Tiny SPRL (<http://tiny.be>).
# © 2015 Agile Business Group <http://www.agilebg.com>
# © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# License GPL-3.0 or later (http://www.gnu.org/licenses/gpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Database Auto-Backup",
"summary": "Backups database",
"version": "10.0.1.0.2",
"version": "11.0.1.0.0",
"author": (
"Yenthe Van Ginneken, "
"Agile Business Group, "
@ -15,11 +14,11 @@
"LasLabs, "
"Odoo Community Association (OCA)"
),
'license': "AGPL-3",
"website": "http://www.vanroey.be/applications/bedrijfsbeheer/odoo",
"license": "AGPL-3",
"website": "https://github.com/OCA/server-tools/",
"category": "Tools",
"depends": [
'mail',
"mail",
],
"data": [
"data/ir_cron.xml",
@ -27,7 +26,6 @@
"security/ir.model.access.csv",
"view/db_backup_view.xml",
],
"application": True,
"installable": True,
"external_dependencies": {
"python": ["pysftp"],

12
auto_backup/data/ir_cron.xml

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="ir_cron_backup_scheduler_0" model="ir.cron">
@ -8,11 +7,10 @@
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="nextcall"
eval="(datetime.now() + timedelta(days=1)).strftime('%Y-%m-%d 02:00:00')"
/>
<field name="model">db.backup</field>
<field name="function">action_backup_all</field>
<field name="nextcall" eval="(datetime.now() + timedelta(days=1)).strftime('%Y-%m-%d 03:00:00')"/>
<field name="model_id" ref="model_db_backup"/>
<field name="state">code</field>
<field name="code">model.action_backup_all()</field>
</record>
</odoo>

3
auto_backup/data/mail_message_subtype.xml

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="mail_message_subtype_success" model="mail.message.subtype">

5
auto_backup/models/__init__.py

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
# © 2004-2009 Tiny SPRL (<http://tiny.be>).
# © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/gpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import db_backup

53
auto_backup/models/db_backup.py

@ -1,18 +1,19 @@
# -*- coding: utf-8 -*-
# © 2004-2009 Tiny SPRL (<http://tiny.be>).
# © 2015 Agile Business Group <http://www.agilebg.com>
# © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/gpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import logging
import os
import shutil
import traceback
from contextlib import contextmanager
from datetime import datetime, timedelta
from glob import iglob
from odoo import exceptions, models, fields, api, _, tools
from odoo import _, api, exceptions, fields, models, tools
from odoo.service import db
import logging
_logger = logging.getLogger(__name__)
try:
import pysftp
@ -21,6 +22,7 @@ except ImportError: # pragma: no cover
class DbBackup(models.Model):
_description = 'Database Backup'
_name = 'db.backup'
_inherit = "mail.thread"
@ -31,59 +33,52 @@ class DbBackup(models.Model):
]
name = fields.Char(
string="Name",
compute="_compute_name",
store=True,
help="Summary of this backup process",
)
folder = fields.Char(
default=lambda self: self._default_folder(),
oldname="bkp_dir",
help='Absolute path for storing the backups',
required=True
)
days_to_keep = fields.Integer(
oldname="daystokeep",
required=True,
default=0,
help="Backups older than this will be deleted automatically. "
"Set 0 to disable autodeletion.",
)
method = fields.Selection(
selection=[("local", "Local disk"), ("sftp", "Remote SFTP server")],
[("local", "Local disk"), ("sftp", "Remote SFTP server")],
default="local",
help="Choose the storage method for this backup.",
)
sftp_host = fields.Char(
string='SFTP Server',
oldname="sftpip",
'SFTP Server',
help=(
"The host name or IP address from your remote"
" server. For example 192.168.0.1"
)
)
sftp_port = fields.Integer(
string="SFTP Port",
"SFTP Port",
default=22,
oldname="sftpport",
help="The port on the FTP server that accepts SSH/SFTP calls."
)
sftp_user = fields.Char(
string='Username in the SFTP Server',
oldname="sftpusername",
'Username in the SFTP Server',
help=(
"The username where the SFTP connection "
"should be made with. This is the user on the external server."
)
)
sftp_password = fields.Char(
string="SFTP Password",
oldname="sftppassword",
"SFTP Password",
help="The password for the SFTP connection. If you specify a private "
"key file, then this is the password to decrypt it.",
)
sftp_private_key = fields.Char(
string="Private key location",
"Private key location",
help="Path to the private key file. Only the Odoo user should have "
"read permissions for that file.",
)
@ -111,9 +106,9 @@ class DbBackup(models.Model):
@api.constrains("folder", "method")
def _check_folder(self):
"""Do not use the filestore or you will backup your backups."""
for s in self:
if (s.method == "local" and
s.folder.startswith(
for record in self:
if (record.method == "local" and
record.folder.startswith(
tools.config.filestore(self.env.cr.dbname))):
raise exceptions.ValidationError(
_("Do not save backups on your filestore, or you will "
@ -200,10 +195,10 @@ class DbBackup(models.Model):
try:
_logger.info("Starting database backup: %s", self.name)
yield
except:
except Exception:
_logger.exception("Database backup failed: %s", self.name)
escaped_tb = tools.html_escape(traceback.format_exc())
self.message_post(
self.message_post( # pylint: disable=translation-required
"<p>%s</p><pre>%s</pre>" % (
_("Database backup failed."),
escaped_tb),
@ -242,23 +237,25 @@ class DbBackup(models.Model):
"""Log a possible cleanup failure."""
self.ensure_one()
try:
_logger.info("Starting cleanup process after database backup: %s",
_logger.info(
"Starting cleanup process after database backup: %s",
self.name)
yield
except:
except Exception:
_logger.exception("Cleanup of old database backups failed: %s")
escaped_tb = tools.html_escape(traceback.format_exc())
self.message_post(
self.message_post( # pylint: disable=translation-required
"<p>%s</p><pre>%s</pre>" % (
_("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",
_logger.info(
"Cleanup of old database backups succeeded: %s",
self.name)
@api.model
def filename(self, when):
@staticmethod
def filename(when):
"""Generate a file name for a backup.
:param datetime.datetime when:

6
auto_backup/tests/__init__.py

@ -1,7 +1,3 @@
# -*- coding: utf-8 -*-
# © 2015 Agile Business Group <http://www.agilebg.com>
# © 2015 Alessio Gerace <alesiso.gerace@agilebg.com>
# © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import test_db_backup

28
auto_backup/tests/test_db_backup.py

@ -1,18 +1,17 @@
# -*- coding: utf-8 -*-
# © 2015 Agile Business Group <http://www.agilebg.com>
# © 2015 Alessio Gerace <alesiso.gerace@agilebg.com>
# © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import os
import mock
from datetime import datetime
from contextlib import contextmanager
from datetime import datetime, timedelta
import mock
from odoo.tests import common
from odoo import exceptions, tools
from odoo.tests import common
try:
import pysftp
@ -127,6 +126,23 @@ class TestDbBackup(common.TransactionCase):
if f >= filename]
self.assertEqual(1, len(generated_backup))
def test_action_backup_local_cleanup(self):
""" Backup local database and cleanup old databases """
rec_id = self.new_record('local')
rec_id.days_to_keep = 1
old_date = datetime.now() - timedelta(days=3)
filename = rec_id.filename(old_date)
rec_id.action_backup()
generated_backup = [f for f in os.listdir(rec_id.folder)
if f >= filename]
self.assertEqual(2, len(generated_backup))
filename = rec_id.filename(datetime.now())
rec_id.action_backup()
generated_backup = [f for f in os.listdir(rec_id.folder)
if f >= filename]
self.assertEqual(1, len(generated_backup))
def test_action_backup_sftp_mkdirs(self):
""" It should create remote dirs """
rec_id = self.new_record()

53
auto_backup/view/db_backup_view.xml

@ -1,14 +1,16 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="view_backup_conf_form">
<field name="name">Automated Backups</field>
<record id="view_backup_conf_form" model="ir.ui.view">
<field name="model">db.backup</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_backup" type="object" string="Execute backup" class="oe_highlight"/>
</header>
<div class="oe_title">
<h1><field name="name"/></h1>
</div>
<group string="Basic backup configuration">
<field name="folder"/>
<field name="days_to_keep"/>
@ -47,27 +49,23 @@
</field>
</record>
<record model="ir.ui.view" id="view_backup_conf_tree">
<field name="name">Automated Backups</field>
<record id="view_backup_conf_tree" model="ir.ui.view">
<field name="model">db.backup</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Backups">
<field name='name'/>
<field name='folder'/>
<field name="sftp_host"/>
<tree>
<field name="name"/>
<field name="folder"/>
<field name="days_to_keep"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_backup_conf_search">
<field name="name">Automated Backups</field>
<record id="view_backup_conf_search" model="ir.ui.view">
<field name="model">db.backup</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Search options">
<field name='name'/>
<field name='folder'/>
<search>
<field name="name"/>
<field name="folder"/>
<field name="sftp_host"/>
</search>
</field>
@ -86,22 +84,11 @@
<!-- Execute backup from "More" menu -->
<record id="action_server_backup" model="ir.actions.server">
<field name="name">Execute backup(s)</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="model_db_backup" />
<field name="code">
object.action_backup()
</field>
</record>
<record model="ir.values" id="action_backup">
<field name="name">Execute backup(s)</field>
<field name="action_id" ref="action_server_backup" />
<field
name="value"
eval="'ir.actions.server,%d' % ref('action_server_backup')" />
<field name="key">action</field>
<field name="model_id" ref="model_db_backup" />
<field name="model">db.backup</field>
<field name="key2">client_action_multi</field>
<field name="binding_model_id" ref="model_db_backup" />
<field name="state">code</field>
<field name="code">records.action_backup()</field>
</record>
</odoo>
Loading…
Cancel
Save