From b7961e60005736ffdc8b1ff6d7e3a7498a422bb6 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 22 Jul 2019 11:02:23 +0300 Subject: [PATCH 1/4] dbbackup: add plugin dbbackup, keeps a synchronized backup of lightningd.sqlite3 --- dbbackup/README.md | 41 +++++++++++++++++ dbbackup/__init__.py | 0 dbbackup/dbbackup.py | 96 +++++++++++++++++++++++++++++++++++++++ dbbackup/requirements.txt | 1 + 4 files changed, 138 insertions(+) create mode 100644 dbbackup/README.md create mode 100644 dbbackup/__init__.py create mode 100755 dbbackup/dbbackup.py create mode 100644 dbbackup/requirements.txt diff --git a/dbbackup/README.md b/dbbackup/README.md new file mode 100644 index 000000000..d02145d96 --- /dev/null +++ b/dbbackup/README.md @@ -0,0 +1,41 @@ +## Summary plugin + +This plugin keeps a synchronized backup of c-lightning's (CL) sqlite3 database. +It uses the db_write hook so that every commit (write) to CL's database is first +written to the backup. This allows recovery of any committed-to channel state, +including HTLCs. This plugin does not backup the seed and is not a complete +node-backup. + +## Installation + +For general plugin installation instructions see the repos main +[README.md](https://github.com/lightningd/plugins/blob/master/README.md#Installation) + +## Options: + +* `--db-backup-file`: path of the backup file + +## Usage + +If the given [`db-backup-file`] doesn't exist yet, it will be created from a +copy of CL's database. + +During startup, any existing backup file is checked to match CL's current +database. If that check fails or initialization fails for other reasons, it will +shutdown CL and log `**BROKEN**`. If the plugin fails in writing to the backup +file, it will trigger CL to crash. + +The backup file is created with rw permission for the owner, it contains +sensitive information, so be a bit careful. When plugin complains about mismatch +between backup and original db, please investigate what caused it before +recovering. + +To recover: shutdown CL and copy the backup to `~/.lighting/lightningd.sqlite3` +File permissions may need to be restored. + +## Testing + +The tests uses c-lightning's pytest framework. To run the tests, you can +link or copy this repository's `/dbbackup` directory into c-lightning repo's +`/test` directory. Then cd into the c-lighting repo directory and to run +test_dbbackup_* tests, run: `DEVELOPER=1 py.test tests/ -s -v -k test_dbbackup_` diff --git a/dbbackup/__init__.py b/dbbackup/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dbbackup/dbbackup.py b/dbbackup/dbbackup.py new file mode 100755 index 000000000..fca9348ea --- /dev/null +++ b/dbbackup/dbbackup.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +from lightning import Plugin +import os +import shutil +import sqlite3 +from stat import S_IRUSR, S_IWUSR + + +plugin = Plugin() +plugin.sqlite_pre_init_cmds = [] +plugin.initted = False + + +class NewBackupFileError(Exception): + def __init__(self, path, e): + self.message = 'Could not create db-backup-file {} : {}'.format(path, e) + + +# Create fresh backup, started as a copy +def new_backup_file(db, backup): + try: + shutil.copyfile(db, backup) + os.chmod(backup, S_IRUSR | S_IWUSR) # rw permission for owner + plugin.log('Creating new db-backup-file: {}'.format(backup)) + except Exception as e: + raise NewBackupFileError(backup, e) + + +@plugin.init() +def init(configuration, options, plugin): + # FIXME: `==` should be changed to `is not None`, see workaround below + if plugin.get_option('db-backup-file') == '': + plugin.log('No db-backup-file specified', 'error') + plugin.rpc.stop() # stop lightningd + + try: + db = os.path.join(configuration['lightning-dir'], 'lightningd.sqlite3') + backup = plugin.get_option('db-backup-file') + + # If backup exist, replay pre_init_cmds on a temporary copy + if os.path.isfile(backup): + plugin.log('Found existing db-backup-file: {} comparing...'.format(backup)) + backup_copy = shutil.copy(backup, backup + '.tmp') + db1 = sqlite3.connect(backup_copy, isolation_level=None) + db2 = sqlite3.connect('file:{}?mode=ro'.format(db), uri=True) # open in read-only + for c in plugin.sqlite_pre_init_cmds: + db1.execute(c) + + # If it then matches orignal db, replace backup with copy ... else abort + dbs_match = [x for x in db1.iterdump()] == [x for x in db2.iterdump()] + db1.close() + db2.close() + if dbs_match: + os.rename(backup_copy, backup) + plugin.log("Existing db-backup-file OK and successfully synced") + else: + plugin.log("Existing db-backup-file differs from original database, i.e. applying" + " pre-init statements (to a copy) didn't make it match the original db", 'error') + os.remove(backup_copy) + plugin.rpc.stop() # stop lightningd + + else: + new_backup_file(db, backup) + + plugin.conn = sqlite3.connect(backup, isolation_level=None) + plugin.initted = True + plugin.log('Initialized') + except Exception as e: + if isinstance(e, NewBackupFileError): + plugin.log(e.message, 'error') + else: + plugin.log('Initialization failed: {}'.format(e), 'error') + + plugin.rpc.stop() # stop lightningd + + +@plugin.hook('db_write') +def db_write(plugin, writes): + if not plugin.initted: + plugin.sqlite_pre_init_cmds += writes + else: + try: + for c in writes: + plugin.conn.execute(c) + except Exception as e: + plugin.log('Failed to write to backup: {}'.format(e), 'error') + # This will `FATAL SIGNAL 6` crash lightningd, but it ensures the failed write + # (here) to backup is also not committed-to in the original database + return False + + return True + + +# Workaround for empty or absent option being (incorrectly?) passed as `null` +plugin.add_option('db-backup-file', '', 'The database backup file.') +plugin.run() diff --git a/dbbackup/requirements.txt b/dbbackup/requirements.txt new file mode 100644 index 000000000..21d25b311 --- /dev/null +++ b/dbbackup/requirements.txt @@ -0,0 +1 @@ +pylightning>=0.0.7.3 From 7ebc996ac6611b0a68e6fda0545ccaf4e0856336 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 22 Jul 2019 11:04:03 +0300 Subject: [PATCH 2/4] dbbackup: add the tests --- dbbackup/tests/__init__.py | 0 dbbackup/tests/lightningd-v102.sqlite3 | Bin 0 -> 155648 bytes dbbackup/tests/lightningd-v102.sqlite3-backup | Bin 0 -> 155648 bytes dbbackup/tests/test_dbbackup.py | 161 ++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 dbbackup/tests/__init__.py create mode 100644 dbbackup/tests/lightningd-v102.sqlite3 create mode 100644 dbbackup/tests/lightningd-v102.sqlite3-backup create mode 100644 dbbackup/tests/test_dbbackup.py diff --git a/dbbackup/tests/__init__.py b/dbbackup/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dbbackup/tests/lightningd-v102.sqlite3 b/dbbackup/tests/lightningd-v102.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..13f932ef15c8cc2566755783491bce964a05b33e GIT binary patch literal 155648 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 Date: Sat, 27 Jul 2019 11:45:44 +0300 Subject: [PATCH 3/4] dbbackup: optimize initial db comparison, increase verbosity of returned write error As suggested by @cdecker Fix README --- dbbackup/README.md | 2 +- dbbackup/dbbackup.py | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/dbbackup/README.md b/dbbackup/README.md index d02145d96..b9e7a83ea 100644 --- a/dbbackup/README.md +++ b/dbbackup/README.md @@ -17,7 +17,7 @@ For general plugin installation instructions see the repos main ## Usage -If the given [`db-backup-file`] doesn't exist yet, it will be created from a +If the given `db-backup-file` doesn't exist yet, it will be created from a copy of CL's database. During startup, any existing backup file is checked to match CL's current diff --git a/dbbackup/dbbackup.py b/dbbackup/dbbackup.py index fca9348ea..433bfe64b 100755 --- a/dbbackup/dbbackup.py +++ b/dbbackup/dbbackup.py @@ -5,7 +5,6 @@ import sqlite3 from stat import S_IRUSR, S_IWUSR - plugin = Plugin() plugin.sqlite_pre_init_cmds = [] plugin.initted = False @@ -26,6 +25,13 @@ def new_backup_file(db, backup): raise NewBackupFileError(backup, e) +def compare_dbs(db1, db2): + for a, b in zip(db1.iterdump(), db2.iterdump()): + if a != b: + return False + return True + + @plugin.init() def init(configuration, options, plugin): # FIXME: `==` should be changed to `is not None`, see workaround below @@ -47,7 +53,7 @@ def init(configuration, options, plugin): db1.execute(c) # If it then matches orignal db, replace backup with copy ... else abort - dbs_match = [x for x in db1.iterdump()] == [x for x in db2.iterdump()] + dbs_match = compare_dbs(db1, db2) db1.close() db2.close() if dbs_match: @@ -79,14 +85,14 @@ def db_write(plugin, writes): if not plugin.initted: plugin.sqlite_pre_init_cmds += writes else: - try: - for c in writes: + for c in writes: + try: plugin.conn.execute(c) - except Exception as e: - plugin.log('Failed to write to backup: {}'.format(e), 'error') - # This will `FATAL SIGNAL 6` crash lightningd, but it ensures the failed write - # (here) to backup is also not committed-to in the original database - return False + except Exception as e: + plugin.log('Failed to write to backup: {}, SQL-statement: {}'.format(e, c), 'error') + # This will `FATAL SIGNAL 6` crash lightningd, but it ensures the failed write + # (here) to backup is also not committed-to in the original database + return False return True From de14f62db7e2eb0310754c9391dbd89c329461ce Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Sat, 27 Jul 2019 14:16:07 +0300 Subject: [PATCH 4/4] dbbbackup: pytest fixups --- dbbackup/tests/test_dbbackup.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/dbbackup/tests/test_dbbackup.py b/dbbackup/tests/test_dbbackup.py index 3524f1ffd..7f790c80a 100644 --- a/dbbackup/tests/test_dbbackup.py +++ b/dbbackup/tests/test_dbbackup.py @@ -25,20 +25,21 @@ def test_dbbackup_init(node_factory, executor): with pytest.raises(ConnectionResetError): l1.start() # wait_for_log only works on running daemon and ours is exiting with rpc.stop() - l1.daemon.is_in_log(r'\*\*BROKEN\*\* plugin-dbbackup.py No db-backup-file specified', start=l1.daemon.logsearch_start) + time.sleep(3) + assert l1.daemon.is_in_log(r'\*\*BROKEN\*\* plugin-dbbackup.py No db-backup-file specified', start=l1.daemon.logsearch_start) # Now with an invalid file path (a directory), should error and shutdown l1.daemon.opts['db-backup-file'] = node_factory.directory with pytest.raises(ConnectionResetError): l1.start() - l1.daemon.is_in_log(r'\*\*BROKEN\*\* plugin-dbbackup.py Could not create db-backup-file', start=l1.daemon.logsearch_start) + assert l1.daemon.is_in_log(r'\*\*BROKEN\*\* plugin-dbbackup.py Could not create db-backup-file', start=l1.daemon.logsearch_start) # Create proper backup backup = os.path.join(node_factory.directory, "lightningd.sqlite3-backup") l1.daemon.opts['db-backup-file'] = backup l1.start() - l1.daemon.is_in_log('plugin-dbbackup.py Creating new db-backup-file: {}'.format(backup), start=l1.daemon.logsearch_start) - l1.daemon.is_in_log(r'plugin-dbbackup.py Initialized', start=l1.daemon.logsearch_start) + assert l1.daemon.is_in_log('plugin-dbbackup.py Creating new db-backup-file: {}'.format(backup), start=l1.daemon.logsearch_start) + assert l1.daemon.is_in_log(r'plugin-dbbackup.py Initialized', start=l1.daemon.logsearch_start) # Disable the plugin, restart and trigger db change so it will differ from the backup l1.stop() @@ -55,9 +56,10 @@ def test_dbbackup_init(node_factory, executor): needle = l1.daemon.logsearch_start db = os.path.join(l1.daemon.lightning_dir, "lightningd.sqlite3") - l1.daemon.is_in_log(r'Found existing db-backup-file: {} comparing...'.format(backup, db), start=needle) - l1.daemon.is_in_log(r'\*\*BROKEN\*\* plugin-dbbackup.py Existing db-backup-file differs from original database', start=needle) - l1.daemon.is_in_log(r'UNUSUAL lightningd(.*): JSON-RPC shutdown', start=needle) + time.sleep(2) + assert l1.daemon.is_in_log(r'Found existing db-backup-file: {} comparing...'.format(backup, db), start=needle) + assert l1.daemon.is_in_log(r'\*\*BROKEN\*\* plugin-dbbackup.py Existing db-backup-file differs from original database', start=needle) + assert l1.daemon.is_in_log(r'UNUSUAL lightningd(.*): JSON-RPC shutdown', start=needle) @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") @@ -116,7 +118,7 @@ def test_dbbackup_write_fail(node_factory, executor): with pytest.raises(RpcError): l1.rpc.newaddr() # Trigger a (post-init) database change # cannot use wait_for_log because daemon is dying - l1.daemon.is_in_log(r'\*\*BROKEN\*\* plugin-dbbackup.py Failed to write to backup:', start=l1.daemon.logsearch_start) + assert l1.daemon.is_in_log(r'\*\*BROKEN\*\* plugin-dbbackup.py Failed to write to backup:', start=l1.daemon.logsearch_start) # un-rename backup file and restart, also tests that failed write # was not committed-to in original database @@ -157,5 +159,5 @@ def test_dbbackup_plugin_kill(node_factory, executor): assert logline is not None pid = int(re.search(r'plugin-manager started\((\d+)\).*dbbackup.py', logline).group(1)) os.kill(pid, signal.SIGTERM) - time.sleep(1) - assert l1.daemon.is_in_log(r'\*\*BROKEN\*\*') + time.sleep(2) + assert l1.daemon.is_in_log(r'\*\*BROKEN\*\* .*')