From f7a263eb0b2d0b740886f2f64744c8f607335746 Mon Sep 17 00:00:00 2001 From: Brian Warner <warner@allmydata.com> Date: Tue, 10 Feb 2009 18:49:10 -0700 Subject: [PATCH] #619: make 'tahoe backup' complain and refuse to run if sqlite is unavailable and --no-backupdb is not passed --- src/allmydata/scripts/backupdb.py | 17 ++++++++++- src/allmydata/scripts/cli.py | 2 +- src/allmydata/scripts/tahoe_backup.py | 5 +++ src/allmydata/test/test_backupdb.py | 6 ++-- src/allmydata/test/test_cli.py | 44 ++++++++++++++++----------- 5 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/allmydata/scripts/backupdb.py b/src/allmydata/scripts/backupdb.py index 51fbda7d..15bb3453 100644 --- a/src/allmydata/scripts/backupdb.py +++ b/src/allmydata/scripts/backupdb.py @@ -50,7 +50,22 @@ def get_backupdb(dbfile, stderr=sys.stderr): from pysqlite2 import dbapi2 sqlite = dbapi2 # .. when this clause does it too except ImportError: - print >>stderr, "sqlite unavailable, not using backupdb" + print >>stderr, """\ +The backup command uses a SQLite database to avoid duplicate uploads, but +I was unable to import a python sqlite library. You have two options: + + 1: Install a python sqlite library. python2.5 and beyond have one built-in. + If you are using python2.4, you can install the 'pysqlite' package, + perhaps with 'apt-get install python-pysqlite2', or 'easy_install + pysqlite', or by installing the 'pysqlite' package from + http://pypi.python.org . Make sure you get the version with support for + SQLite 3. + + 2: run me with the --no-backupdb option to disable use of the database. This + will be somewhat slower, since I will be unable to avoid re-uploading + files that were uploaded in the past, but the basic functionality will be + unimpaired. +""" return None must_create = not os.path.exists(dbfile) diff --git a/src/allmydata/scripts/cli.py b/src/allmydata/scripts/cli.py index de5df036..87d6b279 100644 --- a/src/allmydata/scripts/cli.py +++ b/src/allmydata/scripts/cli.py @@ -193,7 +193,7 @@ class LnOptions(VDriveOptions): class BackupOptions(VDriveOptions): optFlags = [ ("verbose", "v", "Be noisy about what is happening."), - ("no-backupdb", None, "Do not use the backup-database (always upload all files)."), + ("no-backupdb", None, "Do not use the SQLite-based backup-database (always upload all files)."), ("ignore-timestamps", None, "Do not use backupdb timestamps to decide if a local file is unchanged."), ] diff --git a/src/allmydata/scripts/tahoe_backup.py b/src/allmydata/scripts/tahoe_backup.py index c38dd455..6b829c02 100644 --- a/src/allmydata/scripts/tahoe_backup.py +++ b/src/allmydata/scripts/tahoe_backup.py @@ -145,6 +145,11 @@ class BackerUpper: "private", "backupdb.sqlite") bdbfile = os.path.abspath(bdbfile) self.backupdb = backupdb.get_backupdb(bdbfile, stderr) + if not self.backupdb: + # get_backupdb() has already delivered a lengthy speech about + # where to find pysqlite and how to add --no-backupdb + print >>stderr, "ERROR: Unable to import sqlite." + return 1 rootcap, path = get_alias(options.aliases, options.to_dir, DEFAULT_ALIAS) to_url = nodeurl + "uri/%s/" % urllib.quote(rootcap) diff --git a/src/allmydata/test/test_backupdb.py b/src/allmydata/test/test_backupdb.py index 0c209e13..ee2e0168 100644 --- a/src/allmydata/test/test_backupdb.py +++ b/src/allmydata/test/test_backupdb.py @@ -11,7 +11,7 @@ class BackupDB(unittest.TestCase): stderr = StringIO() bdb = backupdb.get_backupdb(dbfile, stderr=stderr) if not bdb: - if "sqlite unavailable" in stderr.getvalue(): + if "I was unable to import a python sqlite library" in stderr.getvalue(): raise unittest.SkipTest("sqlite unavailable, skipping test") return bdb @@ -34,7 +34,7 @@ class BackupDB(unittest.TestCase): stderr_f) self.failUnlessEqual(bdb, None) stderr = stderr_f.getvalue() - if "sqlite unavailable" in stderr: + if "I was unable to import a python sqlite library" in stderr: pass else: self.failUnless("backupdb file is unusable" in stderr) @@ -47,7 +47,7 @@ class BackupDB(unittest.TestCase): bdb = backupdb.get_backupdb(where, stderr_f) self.failUnlessEqual(bdb, None) stderr = stderr_f.getvalue() - if "sqlite unavailable" in stderr: + if "I was unable to import a python sqlite library" in stderr: pass else: self.failUnless(("Unable to create/open backupdb file %s" % where) diff --git a/src/allmydata/test/test_cli.py b/src/allmydata/test/test_cli.py index 6bb855a7..7d171d2d 100644 --- a/src/allmydata/test/test_cli.py +++ b/src/allmydata/test/test_cli.py @@ -636,13 +636,6 @@ class Backup(SystemTestMixin, CLITestMixin, unittest.TestCase): mo = re.search(r"(\d)+ files checked, (\d+) directories checked, (\d+) directories read", out) return [int(s) for s in mo.groups()] - def nosqlite_is_ok(self, err, have_bdb): - if have_bdb: - self.failUnlessEqual(err, "") - else: - self.failUnlessEqual(err.strip(), - "sqlite unavailable, not using backupdb") - def test_backup(self): self.basedir = os.path.dirname(self.mktemp()) @@ -659,11 +652,28 @@ class Backup(SystemTestMixin, CLITestMixin, unittest.TestCase): self.writeto("parent/subdir/bar.txt", "bar\n" * 1000) self.writeto("parent/blah.txt", "blah") + def do_backup(use_backupdb=True, verbose=False): + cmd = ["backup"] + if not have_bdb or not use_backupdb: + cmd.append("--no-backupdb") + if verbose: + cmd.append("--verbose") + cmd.append(source) + cmd.append("tahoe:backups") + return self.do_cli(*cmd) + d = self.set_up_nodes() d.addCallback(lambda res: self.do_cli("create-alias", "tahoe")) - d.addCallback(lambda res: self.do_cli("backup", source, "tahoe:backups")) + + if not have_bdb: + d.addCallback(lambda res: self.do_cli("backup", source, "tahoe:backups")) + def _should_complain((rc, out, err)): + self.failUnless("I was unable to import a python sqlite library" in err, err) + d.addCallback(_should_complain) + + d.addCallback(lambda res: do_backup()) def _check0((rc, out, err)): - self.nosqlite_is_ok(err, have_bdb) + self.failUnlessEqual(err, "") self.failUnlessEqual(rc, 0) fu, fr, dc, dr = self.count_output(out) # foo.txt, bar.txt, blah.txt @@ -707,11 +717,11 @@ class Backup(SystemTestMixin, CLITestMixin, unittest.TestCase): d.addCallback(_check4) - d.addCallback(lambda res: self.do_cli("backup", source, "tahoe:backups")) + d.addCallback(lambda res: do_backup()) def _check4a((rc, out, err)): # second backup should reuse everything, if the backupdb is # available - self.nosqlite_is_ok(err, have_bdb) + self.failUnlessEqual(err, "") self.failUnlessEqual(rc, 0) if have_bdb: fu, fr, dc, dr = self.count_output(out) @@ -736,12 +746,11 @@ class Backup(SystemTestMixin, CLITestMixin, unittest.TestCase): d.addCallback(_reset_last_checked) - d.addCallback(lambda res: - self.do_cli("backup", "--verbose", source, "tahoe:backups")) + d.addCallback(lambda res: do_backup(verbose=True)) def _check4b((rc, out, err)): # we should check all files, and re-use all of them. None of # the directories should have been changed. - self.nosqlite_is_ok(err, have_bdb) + self.failUnlessEqual(err, "") self.failUnlessEqual(rc, 0) fu, fr, dc, dr = self.count_output(out) fchecked, dchecked, dread = self.count_output2(out) @@ -782,12 +791,12 @@ class Backup(SystemTestMixin, CLITestMixin, unittest.TestCase): # turn a directory into a file os.rmdir(os.path.join(source, "empty")) self.writeto("empty", "imagine nothing being here") - return self.do_cli("backup", source, "tahoe:backups") + return do_backup() d.addCallback(_modify) def _check5a((rc, out, err)): # second backup should reuse bar.txt (if backupdb is available), # and upload the rest. None of the directories can be reused. - self.nosqlite_is_ok(err, have_bdb) + self.failUnlessEqual(err, "") self.failUnlessEqual(rc, 0) if have_bdb: fu, fr, dc, dr = self.count_output(out) @@ -825,8 +834,7 @@ class Backup(SystemTestMixin, CLITestMixin, unittest.TestCase): self.failUnlessEqual(out, "foo") d.addCallback(_check8) - d.addCallback(lambda res: - self.do_cli("backup", "--no-backupdb", source, "tahoe:backups")) + d.addCallback(lambda res: do_backup(use_backupdb=False)) def _check9((rc, out, err)): # --no-backupdb means re-upload everything. We still get to # re-use the directories, since nothing changed. -- 2.45.2