1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2025-02-22 22:22:27 +00:00

Improved error handling/reporting.

Closes #12.
This commit is contained in:
Jonas Borgström 2013-12-15 20:35:29 +01:00
parent a56652f5c1
commit 7bacfa2d6e
8 changed files with 46 additions and 42 deletions

View file

@ -8,6 +8,7 @@ Version 0.9
(feature release, released on X) (feature release, released on X)
- Improved error handling / reporting. (#12)
- Use fcntl() instead of flock() when locking repository/cache. (#15) - Use fcntl() instead of flock() when locking repository/cache. (#15)
- Let ssh figure out port/user if not specified so we don't override .ssh/config (#9) - Let ssh figure out port/user if not specified so we don't override .ssh/config (#9)

View file

@ -10,7 +10,7 @@
from io import BytesIO from io import BytesIO
from attic import xattr from attic import xattr
from attic.chunker import chunkify from attic.chunker import chunkify
from attic.helpers import uid2user, user2uid, gid2group, group2gid, \ from attic.helpers import Error, uid2user, user2uid, gid2group, group2gid, \
Statistics, decode_dict, st_mtime_ns, make_path_safe Statistics, decode_dict, st_mtime_ns, make_path_safe
ITEMS_BUFFER = 1024 * 1024 ITEMS_BUFFER = 1024 * 1024
@ -72,11 +72,11 @@ def peek(self):
class Archive(object): class Archive(object):
class DoesNotExist(Exception): class DoesNotExist(Error):
pass """Archive {} does not exist"""
class AlreadyExists(Exception): class AlreadyExists(Error):
pass """Archive {} already exists"""
def __init__(self, repository, key, manifest, name, cache=None, create=False, def __init__(self, repository, key, manifest, name, cache=None, create=False,
checkpoint_interval=300, numeric_owner=False): checkpoint_interval=300, numeric_owner=False):

View file

@ -11,7 +11,7 @@
from attic.repository import Repository from attic.repository import Repository
from attic.cache import Cache from attic.cache import Cache
from attic.key import key_creator from attic.key import key_creator
from attic.helpers import location_validator, format_time, \ from attic.helpers import Error, location_validator, format_time, \
format_file_mode, ExcludePattern, exclude_path, adjust_patterns, to_localtime, \ format_file_mode, ExcludePattern, exclude_path, adjust_patterns, to_localtime, \
get_cache_dir, get_keys_dir, format_timedelta, prune_split, Manifest, Location, remove_surrogates get_cache_dir, get_keys_dir, format_timedelta, prune_split, Manifest, Location, remove_surrogates
from attic.remote import RepositoryServer, RemoteRepository, ConnectionClosed from attic.remote import RepositoryServer, RemoteRepository, ConnectionClosed
@ -477,24 +477,9 @@ def main():
archiver = Archiver() archiver = Archiver()
try: try:
exit_code = archiver.run(sys.argv[1:]) exit_code = archiver.run(sys.argv[1:])
except Repository.DoesNotExist: except Error as e:
archiver.print_error('Error: Repository not found') archiver.print_error(e.get_message())
exit_code = 1 exit_code = e.exit_code
except Repository.AlreadyExists:
archiver.print_error('Error: Repository already exists')
exit_code = 1
except Archive.AlreadyExists as e:
archiver.print_error('Error: Archive "%s" already exists', e)
exit_code = 1
except Archive.DoesNotExist as e:
archiver.print_error('Error: Archive "%s" does not exist', e)
exit_code = 1
except Cache.RepositoryReplay:
archiver.print_error('Cache is newer than repository, refusing to continue')
exit_code = 1
except ConnectionClosed:
archiver.print_error('Connection closed by remote host')
exit_code = 1
except KeyboardInterrupt: except KeyboardInterrupt:
archiver.print_error('Error: Keyboard interrupt') archiver.print_error('Error: Keyboard interrupt')
exit_code = 1 exit_code = 1

View file

@ -5,7 +5,7 @@
from binascii import hexlify from binascii import hexlify
import shutil import shutil
from .helpers import get_cache_dir, decode_dict, st_mtime_ns, unhexlify, UpgradableLock from .helpers import Error, get_cache_dir, decode_dict, st_mtime_ns, unhexlify, UpgradableLock
from .hashindex import ChunkIndex from .hashindex import ChunkIndex
@ -13,9 +13,8 @@ class Cache(object):
"""Client Side cache """Client Side cache
""" """
class RepositoryReplay(Exception): class RepositoryReplay(Error):
""" """Cache is newer than repository, refusing to continue"""
"""
def __init__(self, repository, key, manifest): def __init__(self, repository, key, manifest):
self.timestamp = None self.timestamp = None

View file

@ -14,9 +14,22 @@
import fcntl import fcntl
class Error(Exception):
"""Error base class"""
exit_code = 1
def get_message(self):
return 'Error: ' + type(self).__doc__.format(*self.args)
class UpgradableLock: class UpgradableLock:
class LockUpgradeFailed(Error):
"""Failed to acquire write lock on {}"""
def __init__(self, path, exclusive=False): def __init__(self, path, exclusive=False):
self.path = path
try: try:
self.fd = open(path, 'r+') self.fd = open(path, 'r+')
except IOError: except IOError:
@ -28,7 +41,10 @@ def __init__(self, path, exclusive=False):
self.is_exclusive = exclusive self.is_exclusive = exclusive
def upgrade(self): def upgrade(self):
fcntl.lockf(self.fd, fcntl.LOCK_EX) try:
fcntl.lockf(self.fd, fcntl.LOCK_EX)
except OSError as e:
raise self.LockUpgradeFailed(self.path)
self.is_exclusive = True self.is_exclusive = True
def release(self): def release(self):

View file

@ -4,17 +4,16 @@
import select import select
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import sys import sys
import getpass
from .helpers import Error
from .repository import Repository from .repository import Repository
from .lrucache import LRUCache from .lrucache import LRUCache
BUFSIZE = 10 * 1024 * 1024 BUFSIZE = 10 * 1024 * 1024
class ConnectionClosed(Exception): class ConnectionClosed(Error):
"""Connection closed by remote host """Connection closed by remote host"""
"""
class RepositoryServer(object): class RepositoryServer(object):

View file

@ -7,7 +7,7 @@
from zlib import crc32 from zlib import crc32
from .hashindex import NSIndex from .hashindex import NSIndex
from .helpers import IntegrityError, read_msgpack, write_msgpack, unhexlify, UpgradableLock from .helpers import Error, IntegrityError, read_msgpack, write_msgpack, unhexlify, UpgradableLock
from .lrucache import LRUCache from .lrucache import LRUCache
MAX_OBJECT_SIZE = 20 * 1024 * 1024 MAX_OBJECT_SIZE = 20 * 1024 * 1024
@ -30,11 +30,15 @@ class Repository(object):
DEFAULT_MAX_SEGMENT_SIZE = 5 * 1024 * 1024 DEFAULT_MAX_SEGMENT_SIZE = 5 * 1024 * 1024
DEFAULT_SEGMENTS_PER_DIR = 10000 DEFAULT_SEGMENTS_PER_DIR = 10000
class DoesNotExist(KeyError): class DoesNotExist(Error):
"""Requested key does not exist""" """Repository {} does not exist"""
class AlreadyExists(Error):
"""Repository {} already exists"""
class InvalidRepository(Error):
"""{} is not a valid repository"""
class AlreadyExists(KeyError):
"""Requested key does not exist"""
def __init__(self, path, create=False): def __init__(self, path, create=False):
self.io = None self.io = None
@ -70,11 +74,11 @@ def open(self, path):
self.path = path self.path = path
if not os.path.isdir(path): if not os.path.isdir(path):
raise self.DoesNotExist(path) raise self.DoesNotExist(path)
self.lock = UpgradableLock(os.path.join(path, 'config'))
self.config = RawConfigParser() self.config = RawConfigParser()
self.config.read(os.path.join(self.path, 'config')) self.config.read(os.path.join(self.path, 'config'))
if self.config.getint('repository', 'version') != 1: if not 'repository' in self.config.sections() or self.config.getint('repository', 'version') != 1:
raise Exception('%s Does not look like an Attic repository') raise self.InvalidRepository(path)
self.lock = UpgradableLock(os.path.join(path, 'config'))
self.max_segment_size = self.config.getint('repository', 'max_segment_size') self.max_segment_size = self.config.getint('repository', 'max_segment_size')
self.segments_per_dir = self.config.getint('repository', 'segments_per_dir') self.segments_per_dir = self.config.getint('repository', 'segments_per_dir')
self.id = unhexlify(self.config.get('repository', 'id').strip()) self.id = unhexlify(self.config.get('repository', 'id').strip())

View file

@ -84,5 +84,5 @@ def test_read_only_lock_file(self):
file = tempfile.NamedTemporaryFile() file = tempfile.NamedTemporaryFile()
os.chmod(file.name, 0o444) os.chmod(file.name, 0o444)
lock = UpgradableLock(file.name) lock = UpgradableLock(file.name)
self.assert_raises(OSError, lock.upgrade) self.assert_raises(UpgradableLock.LockUpgradeFailed, lock.upgrade)
lock.release() lock.release()