2010-10-15 18:35:49 +00:00
|
|
|
import argparse
|
2011-08-12 06:49:01 +00:00
|
|
|
from datetime import datetime
|
2011-06-16 19:55:54 +00:00
|
|
|
from operator import attrgetter
|
2010-10-25 18:22:20 +00:00
|
|
|
import os
|
2010-10-30 11:44:25 +00:00
|
|
|
import stat
|
2010-10-16 09:45:36 +00:00
|
|
|
import sys
|
2010-02-23 21:12:22 +00:00
|
|
|
|
2010-10-20 17:59:15 +00:00
|
|
|
from .archive import Archive
|
2010-10-26 19:25:25 +00:00
|
|
|
from .store import Store
|
2010-10-20 17:59:15 +00:00
|
|
|
from .cache import Cache
|
2012-12-04 22:02:10 +00:00
|
|
|
from .key import key_creator
|
2011-08-11 19:18:13 +00:00
|
|
|
from .helpers import location_validator, format_time, \
|
2012-10-17 09:40:23 +00:00
|
|
|
format_file_mode, IncludePattern, ExcludePattern, exclude_path, adjust_patterns, to_localtime, \
|
2011-11-22 20:47:17 +00:00
|
|
|
get_cache_dir, format_timedelta, prune_split, Manifest, Location
|
2010-11-15 21:18:47 +00:00
|
|
|
from .remote import StoreServer, RemoteStore
|
2010-03-15 20:23:34 +00:00
|
|
|
|
2011-10-29 15:01:07 +00:00
|
|
|
|
2010-03-15 20:23:34 +00:00
|
|
|
class Archiver(object):
|
|
|
|
|
2010-10-30 11:44:25 +00:00
|
|
|
def __init__(self):
|
|
|
|
self.exit_code = 0
|
|
|
|
|
2010-11-15 21:18:47 +00:00
|
|
|
def open_store(self, location, create=False):
|
2010-11-17 20:28:13 +00:00
|
|
|
if location.proto == 'ssh':
|
2012-12-04 22:02:10 +00:00
|
|
|
store = RemoteStore(location, create=create)
|
2010-11-15 21:18:47 +00:00
|
|
|
else:
|
2012-12-04 22:02:10 +00:00
|
|
|
store = Store(location.path, create=create)
|
|
|
|
store._location = location
|
|
|
|
return store
|
2010-02-23 20:34:28 +00:00
|
|
|
|
2010-10-30 11:44:25 +00:00
|
|
|
def print_error(self, msg, *args):
|
|
|
|
msg = args and msg % args or msg
|
|
|
|
self.exit_code = 1
|
2012-12-06 22:04:01 +00:00
|
|
|
print >> sys.stderr, 'darc: ' + msg
|
2010-10-30 11:44:25 +00:00
|
|
|
|
|
|
|
def print_verbose(self, msg, *args, **kw):
|
|
|
|
if self.verbose:
|
|
|
|
msg = args and msg % args or msg
|
|
|
|
if kw.get('newline', True):
|
|
|
|
print msg
|
|
|
|
else:
|
|
|
|
print msg,
|
|
|
|
|
2010-11-15 21:18:47 +00:00
|
|
|
def do_serve(self, args):
|
2010-11-17 21:40:39 +00:00
|
|
|
return StoreServer().serve()
|
2010-11-15 21:18:47 +00:00
|
|
|
|
2011-07-30 19:13:48 +00:00
|
|
|
def do_init(self, args):
|
2011-10-27 20:17:47 +00:00
|
|
|
print 'Initializing store "%s"' % args.store.orig
|
2011-07-30 19:13:48 +00:00
|
|
|
store = self.open_store(args.store, create=True)
|
2012-12-04 22:02:10 +00:00
|
|
|
key = key_creator(store, args)
|
|
|
|
manifest = Manifest()
|
|
|
|
manifest.store = store
|
|
|
|
manifest.key = key
|
2011-09-04 21:02:47 +00:00
|
|
|
manifest.write()
|
|
|
|
store.commit()
|
2011-08-06 11:01:58 +00:00
|
|
|
return self.exit_code
|
2011-07-30 19:13:48 +00:00
|
|
|
|
2012-12-04 22:02:10 +00:00
|
|
|
def do_change_passphrase(self, args):
|
|
|
|
store = self.open_store(Location(args.store))
|
|
|
|
manifest, key = Manifest.load(store)
|
|
|
|
key.change_passphrase()
|
2011-10-27 20:17:47 +00:00
|
|
|
return self.exit_code
|
|
|
|
|
2010-10-15 18:35:49 +00:00
|
|
|
def do_create(self, args):
|
2011-08-07 15:10:21 +00:00
|
|
|
t0 = datetime.now()
|
2011-07-30 19:13:48 +00:00
|
|
|
store = self.open_store(args.archive)
|
2012-12-04 22:02:10 +00:00
|
|
|
manifest, key = Manifest.load(store)
|
2011-09-04 21:02:47 +00:00
|
|
|
cache = Cache(store, key, manifest)
|
2011-09-10 15:19:02 +00:00
|
|
|
archive = Archive(store, key, manifest, args.archive.archive, cache=cache,
|
2012-02-29 22:59:17 +00:00
|
|
|
create=True, checkpoint_interval=args.checkpoint_interval,
|
|
|
|
numeric_owner=args.numeric_owner)
|
2010-11-09 19:49:21 +00:00
|
|
|
# Add darc cache dir to inode_skip list
|
2011-01-04 22:16:55 +00:00
|
|
|
skip_inodes = set()
|
2010-11-09 19:49:21 +00:00
|
|
|
try:
|
2011-08-06 11:01:58 +00:00
|
|
|
st = os.stat(get_cache_dir())
|
2011-01-04 22:16:55 +00:00
|
|
|
skip_inodes.add((st.st_ino, st.st_dev))
|
2010-11-09 19:49:21 +00:00
|
|
|
except IOError:
|
|
|
|
pass
|
|
|
|
# Add local store dir to inode_skip list
|
|
|
|
if not args.archive.host:
|
|
|
|
try:
|
|
|
|
st = os.stat(args.archive.path)
|
2011-01-04 22:16:55 +00:00
|
|
|
skip_inodes.add((st.st_ino, st.st_dev))
|
2010-11-09 19:49:21 +00:00
|
|
|
except IOError:
|
|
|
|
pass
|
2010-10-30 11:44:25 +00:00
|
|
|
for path in args.paths:
|
2012-02-04 16:32:46 +00:00
|
|
|
if args.dontcross:
|
|
|
|
try:
|
|
|
|
restrict_dev = os.lstat(path).st_dev
|
|
|
|
except OSError, e:
|
|
|
|
self.print_error('%s: %s', path, e)
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
restrict_dev = None
|
|
|
|
self._process(archive, cache, args.patterns, skip_inodes, path, restrict_dev)
|
2011-09-10 15:19:02 +00:00
|
|
|
archive.save()
|
2011-08-07 15:10:21 +00:00
|
|
|
if args.stats:
|
|
|
|
t = datetime.now()
|
|
|
|
diff = t - t0
|
|
|
|
print '-' * 40
|
|
|
|
print 'Archive name: %s' % args.archive.archive
|
2011-08-15 20:32:26 +00:00
|
|
|
print 'Archive fingerprint: %s' % archive.id.encode('hex')
|
2011-08-07 15:10:21 +00:00
|
|
|
print 'Start time: %s' % t0.strftime('%c')
|
|
|
|
print 'End time: %s' % t.strftime('%c')
|
2011-08-11 19:18:13 +00:00
|
|
|
print 'Duration: %s' % format_timedelta(diff)
|
2011-08-07 15:10:21 +00:00
|
|
|
archive.stats.print_()
|
|
|
|
print '-' * 40
|
2010-10-30 11:44:25 +00:00
|
|
|
return self.exit_code
|
2010-02-20 21:28:46 +00:00
|
|
|
|
2012-02-04 16:32:46 +00:00
|
|
|
def _process(self, archive, cache, patterns, skip_inodes, path, restrict_dev):
|
2010-11-23 11:50:09 +00:00
|
|
|
if exclude_path(path, patterns):
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
st = os.lstat(path)
|
|
|
|
except OSError, e:
|
|
|
|
self.print_error('%s: %s', path, e)
|
|
|
|
return
|
2011-01-04 22:16:55 +00:00
|
|
|
if (st.st_ino, st.st_dev) in skip_inodes:
|
|
|
|
return
|
2012-02-04 16:32:46 +00:00
|
|
|
# Entering a new filesystem?
|
|
|
|
if restrict_dev and st.st_dev != restrict_dev:
|
|
|
|
return
|
2011-08-07 17:44:13 +00:00
|
|
|
# Ignore unix sockets
|
|
|
|
if stat.S_ISSOCK(st.st_mode):
|
|
|
|
return
|
2010-11-23 11:50:09 +00:00
|
|
|
self.print_verbose(path)
|
2012-03-03 13:02:22 +00:00
|
|
|
if stat.S_ISREG(st.st_mode):
|
|
|
|
try:
|
|
|
|
archive.process_file(path, st, cache)
|
|
|
|
except IOError, e:
|
|
|
|
self.print_error('%s: %s', path, e)
|
|
|
|
elif stat.S_ISDIR(st.st_mode):
|
2012-03-01 21:35:43 +00:00
|
|
|
archive.process_item(path, st)
|
2010-11-23 11:50:09 +00:00
|
|
|
try:
|
|
|
|
entries = os.listdir(path)
|
|
|
|
except OSError, e:
|
|
|
|
self.print_error('%s: %s', path, e)
|
|
|
|
else:
|
2011-07-01 20:01:24 +00:00
|
|
|
for filename in sorted(entries):
|
2011-01-04 22:16:55 +00:00
|
|
|
self._process(archive, cache, patterns, skip_inodes,
|
2012-02-04 16:32:46 +00:00
|
|
|
os.path.join(path, filename), restrict_dev)
|
2010-11-23 11:50:09 +00:00
|
|
|
elif stat.S_ISLNK(st.st_mode):
|
|
|
|
archive.process_symlink(path, st)
|
2012-03-03 13:02:22 +00:00
|
|
|
elif stat.S_ISFIFO(st.st_mode):
|
|
|
|
archive.process_item(path, st)
|
|
|
|
elif stat.S_ISCHR(st.st_mode) or stat.S_ISBLK(st.st_mode):
|
|
|
|
archive.process_dev(path, st)
|
2010-11-23 11:50:09 +00:00
|
|
|
else:
|
|
|
|
self.print_error('Unknown file type: %s', path)
|
|
|
|
|
2010-10-15 18:35:49 +00:00
|
|
|
def do_extract(self, args):
|
2012-10-17 09:40:23 +00:00
|
|
|
store = self.open_store(args.archive)
|
2012-12-04 22:02:10 +00:00
|
|
|
manifest, key = Manifest.load(store)
|
2012-10-17 09:40:23 +00:00
|
|
|
archive = Archive(store, key, manifest, args.archive.archive,
|
|
|
|
numeric_owner=args.numeric_owner)
|
|
|
|
dirs = []
|
2012-11-27 23:03:35 +00:00
|
|
|
for item, peek in archive.iter_items(lambda item: not exclude_path(item['path'], args.patterns)):
|
2012-12-06 22:04:01 +00:00
|
|
|
while dirs and not item['path'].startswith(dirs[-1]['path']):
|
2012-10-17 09:40:23 +00:00
|
|
|
archive.extract_item(dirs.pop(-1), args.dest)
|
2012-12-06 22:04:01 +00:00
|
|
|
self.print_verbose(item['path'])
|
|
|
|
try:
|
|
|
|
if stat.S_ISDIR(item['mode']):
|
|
|
|
dirs.append(item)
|
|
|
|
archive.extract_item(item, args.dest, restore_attrs=False)
|
|
|
|
else:
|
|
|
|
archive.extract_item(item, args.dest, peek=peek)
|
|
|
|
except IOError, e:
|
|
|
|
self.print_error('%s: %s', item['path'], e)
|
|
|
|
|
2010-10-31 19:12:32 +00:00
|
|
|
while dirs:
|
|
|
|
archive.extract_item(dirs.pop(-1), args.dest)
|
2010-10-30 11:44:25 +00:00
|
|
|
return self.exit_code
|
2010-02-24 22:24:19 +00:00
|
|
|
|
2010-10-15 18:35:49 +00:00
|
|
|
def do_delete(self, args):
|
2010-10-21 19:21:43 +00:00
|
|
|
store = self.open_store(args.archive)
|
2012-12-04 22:02:10 +00:00
|
|
|
manifest, key = Manifest.load(store)
|
2011-09-04 21:02:47 +00:00
|
|
|
cache = Cache(store, key, manifest)
|
|
|
|
archive = Archive(store, key, manifest, args.archive.archive, cache=cache)
|
2010-10-15 18:35:49 +00:00
|
|
|
archive.delete(cache)
|
2010-10-30 11:44:25 +00:00
|
|
|
return self.exit_code
|
2010-02-24 22:24:19 +00:00
|
|
|
|
2010-10-15 18:35:49 +00:00
|
|
|
def do_list(self, args):
|
2010-10-21 19:21:43 +00:00
|
|
|
store = self.open_store(args.src)
|
2012-12-04 22:02:10 +00:00
|
|
|
manifest, key = Manifest.load(store)
|
2010-10-15 18:35:49 +00:00
|
|
|
if args.src.archive:
|
2010-10-30 11:44:25 +00:00
|
|
|
tmap = {1: 'p', 2: 'c', 4: 'd', 6: 'b', 010: '-', 012: 'l', 014: 's'}
|
2011-09-04 21:02:47 +00:00
|
|
|
archive = Archive(store, key, manifest, args.src.archive)
|
2012-11-27 23:03:35 +00:00
|
|
|
for item, _ in archive.iter_items():
|
2012-10-17 09:40:23 +00:00
|
|
|
type = tmap.get(item['mode'] / 4096, '?')
|
|
|
|
mode = format_file_mode(item['mode'])
|
|
|
|
size = 0
|
|
|
|
if type == '-':
|
|
|
|
try:
|
|
|
|
size = sum(size for _, size, _ in item['chunks'])
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
mtime = format_time(datetime.fromtimestamp(item['mtime']))
|
|
|
|
if 'source' in item:
|
|
|
|
if type == 'l':
|
|
|
|
extra = ' -> %s' % item['source']
|
|
|
|
else:
|
|
|
|
type = 'h'
|
|
|
|
extra = ' link to %s' % item['source']
|
|
|
|
else:
|
|
|
|
extra = ''
|
|
|
|
print '%s%s %-6s %-6s %8d %s %s%s' % (type, mode, item['user'] or item['uid'],
|
|
|
|
item['group'] or item['gid'], size, mtime,
|
|
|
|
item['path'], extra)
|
2010-10-15 18:35:49 +00:00
|
|
|
else:
|
2011-09-04 21:02:47 +00:00
|
|
|
for archive in sorted(Archive.list_archives(store, key, manifest), key=attrgetter('ts')):
|
2011-06-16 19:55:54 +00:00
|
|
|
print '%-20s %s' % (archive.metadata['name'], to_localtime(archive.ts).strftime('%c'))
|
2010-10-30 11:44:25 +00:00
|
|
|
return self.exit_code
|
2010-02-24 22:24:19 +00:00
|
|
|
|
2010-10-15 18:35:49 +00:00
|
|
|
def do_verify(self, args):
|
2010-10-21 19:21:43 +00:00
|
|
|
store = self.open_store(args.archive)
|
2012-12-04 22:02:10 +00:00
|
|
|
manifest, key = Manifest.load(store)
|
2011-09-04 21:02:47 +00:00
|
|
|
archive = Archive(store, key, manifest, args.archive.archive)
|
2011-10-29 15:01:07 +00:00
|
|
|
|
2011-07-17 21:53:23 +00:00
|
|
|
def start_cb(item):
|
2011-08-07 10:37:17 +00:00
|
|
|
self.print_verbose('%s ...', item['path'], newline=False)
|
2011-10-29 15:01:07 +00:00
|
|
|
|
2011-07-17 21:53:23 +00:00
|
|
|
def result_cb(item, success):
|
|
|
|
if success:
|
|
|
|
self.print_verbose('OK')
|
|
|
|
else:
|
|
|
|
self.print_verbose('ERROR')
|
|
|
|
self.print_error('%s: verification failed' % item['path'])
|
2012-11-27 23:03:35 +00:00
|
|
|
for item, peek in archive.iter_items(lambda item: not exclude_path(item['path'], args.patterns)):
|
2011-07-31 16:37:27 +00:00
|
|
|
if stat.S_ISREG(item['mode']) and 'chunks' in item:
|
2012-11-27 23:03:35 +00:00
|
|
|
archive.verify_file(item, start_cb, result_cb, peek=peek)
|
2010-10-30 11:44:25 +00:00
|
|
|
return self.exit_code
|
2010-02-27 22:23:39 +00:00
|
|
|
|
2010-10-15 18:35:49 +00:00
|
|
|
def do_info(self, args):
|
2010-10-21 19:21:43 +00:00
|
|
|
store = self.open_store(args.archive)
|
2012-12-04 22:02:10 +00:00
|
|
|
manifest, key = Manifest.load(store)
|
2011-09-04 21:02:47 +00:00
|
|
|
cache = Cache(store, key, manifest)
|
|
|
|
archive = Archive(store, key, manifest, args.archive.archive, cache=cache)
|
2011-08-07 15:10:21 +00:00
|
|
|
stats = archive.calc_stats(cache)
|
2011-08-07 18:00:18 +00:00
|
|
|
print 'Name:', archive.name
|
2011-08-15 20:32:26 +00:00
|
|
|
print 'Fingerprint: %s' % archive.id.encode('hex')
|
2010-10-25 17:57:54 +00:00
|
|
|
print 'Hostname:', archive.metadata['hostname']
|
|
|
|
print 'Username:', archive.metadata['username']
|
2011-08-07 18:00:18 +00:00
|
|
|
print 'Time:', to_localtime(archive.ts).strftime('%c')
|
2010-10-25 17:57:54 +00:00
|
|
|
print 'Command line:', ' '.join(archive.metadata['cmdline'])
|
2011-08-07 15:10:21 +00:00
|
|
|
stats.print_()
|
2010-10-30 11:44:25 +00:00
|
|
|
return self.exit_code
|
2010-04-18 20:34:21 +00:00
|
|
|
|
2011-11-22 20:47:17 +00:00
|
|
|
def do_prune(self, args):
|
2011-08-06 21:33:06 +00:00
|
|
|
store = self.open_store(args.store)
|
2012-12-04 22:02:10 +00:00
|
|
|
manifest, key = Manifest.load(store)
|
2011-09-04 21:02:47 +00:00
|
|
|
cache = Cache(store, key, manifest)
|
|
|
|
archives = list(sorted(Archive.list_archives(store, key, manifest, cache),
|
2011-08-06 21:33:06 +00:00
|
|
|
key=attrgetter('ts'), reverse=True))
|
2011-08-21 20:17:00 +00:00
|
|
|
if args.hourly + args.daily + args.weekly + args.monthly + args.yearly == 0:
|
|
|
|
self.print_error('At least one of the "hourly", "daily", "weekly", "monthly" or "yearly" '
|
2011-08-06 21:33:06 +00:00
|
|
|
'settings must be specified')
|
|
|
|
return 1
|
2011-08-11 19:18:13 +00:00
|
|
|
if args.prefix:
|
|
|
|
archives = [archive for archive in archives if archive.name.startswith(args.prefix)]
|
2011-08-16 20:02:42 +00:00
|
|
|
keep = []
|
2011-08-21 20:17:00 +00:00
|
|
|
if args.hourly:
|
2011-11-22 20:47:17 +00:00
|
|
|
keep += prune_split(archives, '%Y-%m-%d %H', args.hourly)
|
2011-08-11 19:18:13 +00:00
|
|
|
if args.daily:
|
2011-11-22 20:47:17 +00:00
|
|
|
keep += prune_split(archives, '%Y-%m-%d', args.daily, keep)
|
2011-08-12 06:49:01 +00:00
|
|
|
if args.weekly:
|
2012-12-10 19:48:39 +00:00
|
|
|
keep += prune_split(archives, '%G-%V', args.weekly, keep)
|
2011-08-12 06:49:01 +00:00
|
|
|
if args.monthly:
|
2011-11-22 20:47:17 +00:00
|
|
|
keep += prune_split(archives, '%Y-%m', args.monthly, keep)
|
2011-08-12 06:49:01 +00:00
|
|
|
if args.yearly:
|
2011-11-22 20:47:17 +00:00
|
|
|
keep += prune_split(archives, '%Y', args.yearly, keep)
|
2011-08-16 20:02:42 +00:00
|
|
|
|
|
|
|
keep.sort(key=attrgetter('ts'), reverse=True)
|
|
|
|
to_delete = [a for a in archives if a not in keep]
|
2011-08-11 19:18:13 +00:00
|
|
|
|
2011-08-16 20:02:42 +00:00
|
|
|
for archive in keep:
|
|
|
|
self.print_verbose('Keeping archive "%s"' % archive.name)
|
2011-08-11 19:18:13 +00:00
|
|
|
for archive in to_delete:
|
2012-12-04 22:02:10 +00:00
|
|
|
self.print_verbose('Pruning archive "%s"', archive.name)
|
2011-11-22 20:52:31 +00:00
|
|
|
archive.delete(cache)
|
2011-08-06 21:33:06 +00:00
|
|
|
return self.exit_code
|
|
|
|
|
2010-10-16 09:45:36 +00:00
|
|
|
def run(self, args=None):
|
2011-01-04 22:00:39 +00:00
|
|
|
dot_path = os.path.join(os.path.expanduser('~'), '.darc')
|
|
|
|
if not os.path.exists(dot_path):
|
|
|
|
os.mkdir(dot_path)
|
2011-07-30 19:13:48 +00:00
|
|
|
os.mkdir(os.path.join(dot_path, 'keys'))
|
|
|
|
os.mkdir(os.path.join(dot_path, 'cache'))
|
2011-09-10 15:32:05 +00:00
|
|
|
common_parser = argparse.ArgumentParser(add_help=False)
|
|
|
|
common_parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
|
2010-10-15 18:35:49 +00:00
|
|
|
default=False,
|
|
|
|
help='Verbose output')
|
|
|
|
|
2011-09-10 15:32:05 +00:00
|
|
|
parser = argparse.ArgumentParser(description='DARC - Deduplicating Archiver')
|
2010-10-15 18:35:49 +00:00
|
|
|
subparsers = parser.add_subparsers(title='Available subcommands')
|
2010-10-23 21:01:12 +00:00
|
|
|
|
2011-09-10 15:32:05 +00:00
|
|
|
subparser = subparsers.add_parser('serve', parents=[common_parser])
|
2010-11-15 21:18:47 +00:00
|
|
|
subparser.set_defaults(func=self.do_serve)
|
|
|
|
|
2011-09-10 15:32:05 +00:00
|
|
|
subparser = subparsers.add_parser('init', parents=[common_parser])
|
2011-07-30 19:13:48 +00:00
|
|
|
subparser.set_defaults(func=self.do_init)
|
2011-08-06 11:01:58 +00:00
|
|
|
subparser.add_argument('store',
|
2011-07-30 19:13:48 +00:00
|
|
|
type=location_validator(archive=False),
|
|
|
|
help='Store to create')
|
2012-12-04 22:02:10 +00:00
|
|
|
subparser.add_argument('--key-file', dest='keyfile',
|
|
|
|
action='store_true', default=False,
|
|
|
|
help='Encrypt data using key file')
|
|
|
|
subparser.add_argument('--passphrase', dest='passphrase',
|
|
|
|
action='store_true', default=False,
|
|
|
|
help='Encrypt data using passphrase derived key')
|
2011-07-30 19:13:48 +00:00
|
|
|
|
2012-12-04 22:02:10 +00:00
|
|
|
subparser = subparsers.add_parser('change-passphrase', parents=[common_parser])
|
|
|
|
subparser.set_defaults(func=self.do_change_passphrase)
|
|
|
|
subparser.add_argument('store', type=location_validator(archive=False))
|
2011-10-27 20:17:47 +00:00
|
|
|
|
2011-09-10 15:32:05 +00:00
|
|
|
subparser = subparsers.add_parser('create', parents=[common_parser])
|
2010-10-15 18:35:49 +00:00
|
|
|
subparser.set_defaults(func=self.do_create)
|
2011-08-07 15:10:21 +00:00
|
|
|
subparser.add_argument('-s', '--stats', dest='stats',
|
|
|
|
action='store_true', default=False,
|
|
|
|
help='Print statistics for the created archive')
|
2010-11-02 21:47:39 +00:00
|
|
|
subparser.add_argument('-i', '--include', dest='patterns',
|
|
|
|
type=IncludePattern, action='append',
|
|
|
|
help='Include condition')
|
|
|
|
subparser.add_argument('-e', '--exclude', dest='patterns',
|
|
|
|
type=ExcludePattern, action='append',
|
|
|
|
help='Include condition')
|
2011-09-10 15:19:02 +00:00
|
|
|
subparser.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval',
|
|
|
|
type=int, default=300, metavar='SECONDS',
|
|
|
|
help='Write checkpointe ever SECONDS seconds (Default: 300)')
|
2012-02-04 16:32:46 +00:00
|
|
|
subparser.add_argument('--do-not-cross-mountpoints', dest='dontcross',
|
|
|
|
action='store_true', default=False,
|
|
|
|
help='Do not cross mount points')
|
2012-02-29 22:59:17 +00:00
|
|
|
subparser.add_argument('--numeric-owner', dest='numeric_owner',
|
|
|
|
action='store_true', default=False,
|
|
|
|
help='Only store numeric user and group identifiers')
|
2010-10-15 18:35:49 +00:00
|
|
|
subparser.add_argument('archive', metavar='ARCHIVE',
|
|
|
|
type=location_validator(archive=True),
|
|
|
|
help='Archive to create')
|
2012-03-03 12:56:51 +00:00
|
|
|
subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
|
|
|
|
default=['.'], help='Paths to add to archive')
|
2010-10-15 18:35:49 +00:00
|
|
|
|
2011-09-10 15:32:05 +00:00
|
|
|
subparser = subparsers.add_parser('extract', parents=[common_parser])
|
2010-10-15 18:35:49 +00:00
|
|
|
subparser.set_defaults(func=self.do_extract)
|
2010-11-02 21:47:39 +00:00
|
|
|
subparser.add_argument('-i', '--include', dest='patterns',
|
|
|
|
type=IncludePattern, action='append',
|
|
|
|
help='Include condition')
|
|
|
|
subparser.add_argument('-e', '--exclude', dest='patterns',
|
|
|
|
type=ExcludePattern, action='append',
|
|
|
|
help='Include condition')
|
2012-02-29 22:59:17 +00:00
|
|
|
subparser.add_argument('--numeric-owner', dest='numeric_owner',
|
|
|
|
action='store_true', default=False,
|
|
|
|
help='Only obey numeric user and group identifiers')
|
2010-10-15 18:35:49 +00:00
|
|
|
subparser.add_argument('archive', metavar='ARCHIVE',
|
|
|
|
type=location_validator(archive=True),
|
|
|
|
help='Archive to create')
|
|
|
|
subparser.add_argument('dest', metavar='DEST', type=str, nargs='?',
|
|
|
|
help='Where to extract files')
|
|
|
|
|
2011-09-10 15:32:05 +00:00
|
|
|
subparser = subparsers.add_parser('delete', parents=[common_parser])
|
2010-10-15 18:35:49 +00:00
|
|
|
subparser.set_defaults(func=self.do_delete)
|
|
|
|
subparser.add_argument('archive', metavar='ARCHIVE',
|
|
|
|
type=location_validator(archive=True),
|
|
|
|
help='Archive to delete')
|
|
|
|
|
2011-09-10 15:32:05 +00:00
|
|
|
subparser = subparsers.add_parser('list', parents=[common_parser])
|
2010-10-15 18:35:49 +00:00
|
|
|
subparser.set_defaults(func=self.do_list)
|
|
|
|
subparser.add_argument('src', metavar='SRC', type=location_validator(),
|
|
|
|
help='Store/Archive to list contents of')
|
|
|
|
|
2011-09-12 19:34:09 +00:00
|
|
|
subparser = subparsers.add_parser('verify', parents=[common_parser])
|
2010-10-15 18:35:49 +00:00
|
|
|
subparser.set_defaults(func=self.do_verify)
|
2011-07-02 17:25:29 +00:00
|
|
|
subparser.add_argument('-i', '--include', dest='patterns',
|
|
|
|
type=IncludePattern, action='append',
|
|
|
|
help='Include condition')
|
|
|
|
subparser.add_argument('-e', '--exclude', dest='patterns',
|
|
|
|
type=ExcludePattern, action='append',
|
|
|
|
help='Include condition')
|
2010-10-15 18:35:49 +00:00
|
|
|
subparser.add_argument('archive', metavar='ARCHIVE',
|
|
|
|
type=location_validator(archive=True),
|
|
|
|
help='Archive to verity integrity of')
|
|
|
|
|
2011-09-10 15:32:05 +00:00
|
|
|
subparser = subparsers.add_parser('info', parents=[common_parser])
|
2010-10-15 18:35:49 +00:00
|
|
|
subparser.set_defaults(func=self.do_info)
|
|
|
|
subparser.add_argument('archive', metavar='ARCHIVE',
|
|
|
|
type=location_validator(archive=True),
|
|
|
|
help='Archive to display information about')
|
|
|
|
|
2011-11-22 20:47:17 +00:00
|
|
|
subparser = subparsers.add_parser('prune', parents=[common_parser])
|
|
|
|
subparser.set_defaults(func=self.do_prune)
|
2011-08-21 20:17:00 +00:00
|
|
|
subparser.add_argument('-H', '--hourly', dest='hourly', type=int, default=0,
|
|
|
|
help='Number of hourly archives to keep')
|
2011-08-06 21:33:06 +00:00
|
|
|
subparser.add_argument('-d', '--daily', dest='daily', type=int, default=0,
|
|
|
|
help='Number of daily archives to keep')
|
|
|
|
subparser.add_argument('-w', '--weekly', dest='weekly', type=int, default=0,
|
|
|
|
help='Number of daily archives to keep')
|
|
|
|
subparser.add_argument('-m', '--monthly', dest='monthly', type=int, default=0,
|
|
|
|
help='Number of monthly archives to keep')
|
2011-08-07 12:04:14 +00:00
|
|
|
subparser.add_argument('-y', '--yearly', dest='yearly', type=int, default=0,
|
|
|
|
help='Number of yearly archives to keep')
|
|
|
|
subparser.add_argument('-p', '--prefix', dest='prefix', type=str,
|
|
|
|
help='Only consider archive names starting with this prefix')
|
2011-08-06 21:33:06 +00:00
|
|
|
subparser.add_argument('store', metavar='STORE',
|
|
|
|
type=location_validator(archive=False),
|
2011-11-22 20:47:17 +00:00
|
|
|
help='Store to prune')
|
2011-08-06 21:33:06 +00:00
|
|
|
|
2010-10-16 09:45:36 +00:00
|
|
|
args = parser.parse_args(args)
|
2012-10-17 09:40:23 +00:00
|
|
|
if getattr(args, 'patterns', None):
|
|
|
|
adjust_patterns(args.patterns)
|
2010-10-30 11:44:25 +00:00
|
|
|
self.verbose = args.verbose
|
2010-10-16 09:45:36 +00:00
|
|
|
return args.func(args)
|
2010-03-06 17:25:35 +00:00
|
|
|
|
2011-10-29 15:01:07 +00:00
|
|
|
|
2010-02-20 17:23:46 +00:00
|
|
|
def main():
|
|
|
|
archiver = Archiver()
|
2012-12-09 22:06:33 +00:00
|
|
|
try:
|
|
|
|
exit_code = archiver.run()
|
|
|
|
except Store.DoesNotExist:
|
|
|
|
archiver.print_error('Error: Store not found')
|
|
|
|
exit_code = 1
|
|
|
|
except Store.AlreadyExists:
|
|
|
|
archiver.print_error('Error: Store already exists')
|
|
|
|
exit_code = 1
|
|
|
|
except Archive.AlreadyExists, e:
|
|
|
|
archiver.print_error('Error: Archive "%s" already exists', e)
|
|
|
|
exit_code = 1
|
|
|
|
except Archive.DoesNotExist, e:
|
|
|
|
archiver.print_error('Error: Archive "%s" does not exist', e)
|
|
|
|
exit_code = 1
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
archiver.print_error('Error: Keyboard interrupt')
|
|
|
|
exit_code = 1
|
|
|
|
else:
|
|
|
|
if exit_code:
|
|
|
|
archiver.print_error('Exiting with failure status due to previous errors')
|
2012-12-06 22:04:01 +00:00
|
|
|
sys.exit(exit_code)
|
2010-02-20 17:23:46 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2010-03-06 17:25:35 +00:00
|
|
|
main()
|