Back to index

obnam  1.1
Public Member Functions | Public Attributes
obnamlib.plugins.backup_plugin.BackupPlugin Class Reference
Inheritance diagram for obnamlib.plugins.backup_plugin.BackupPlugin:
Inheritance graph
[legend]
Collaboration diagram for obnamlib.plugins.backup_plugin.BackupPlugin:
Collaboration graph
[legend]

List of all members.

Public Member Functions

def enable
def configure_ttystatus_for_backup
def configure_ttystatus_for_checkpoint_removal
def update_progress_with_file
def update_progress_with_upload
def report_stats
def error
def parse_checkpoint_size
def pretend
def backup
def unlock_when_error
def add_chunks_to_shared
def add_client
def compile_exclusion_patterns
def backup_roots
def time_for_checkpoint
def make_checkpoint
def find_files
def can_be_backed_up
def needs_backup
def backup_parents
def backup_metadata
def backup_file_contents
def backup_file_chunk
def backup_dir_contents
def remove_old_roots

Public Attributes

 file_count
 uploaded_bytes
 errors
 started
 memory_dump_counter
 repo
 chunkid_pool
 fs
 exclude_pats
 checkpoints
 last_checkpoint
 interval
 root_metadata
 bad_deduplicate_reported

Detailed Description

Definition at line 58 of file backup_plugin.py.


Member Function Documentation

Definition at line 294 of file backup_plugin.py.

00294 
00295     def add_chunks_to_shared(self):
00296         for chunkid, checksum in self.chunkid_pool:
00297             self.repo.put_chunk_in_shared_trees(chunkid, checksum)
00298         self.chunkid_pool.clear()

Here is the caller graph for this function:

def obnamlib.plugins.backup_plugin.BackupPlugin.add_client (   self,
  client_name 
)

Definition at line 299 of file backup_plugin.py.

00299 
00300     def add_client(self, client_name):
00301         self.repo.lock_root()
00302         if client_name not in self.repo.list_clients():
00303             tracing.trace('adding new client %s' % client_name)
00304             tracing.trace('client list before adding: %s' % 
00305                             self.repo.list_clients())
00306             self.repo.add_client(client_name)
00307             tracing.trace('client list after adding: %s' % 
00308                             self.repo.list_clients())
00309         self.repo.commit_root()
00310         self.repo = self.app.open_repository(repofs=self.repo.fs.fs)

Backup data to repository.

Definition at line 209 of file backup_plugin.py.

00209 
00210     def backup(self, args):
00211         '''Backup data to repository.'''
00212         logging.info('Backup starts')
00213         logging.info('Checkpoints every %s bytes' % 
00214                         self.app.settings['checkpoint'])
00215 
00216         self.app.settings.require('repository')
00217         self.app.settings.require('client-name')
00218         
00219         if not self.app.settings['repository']:
00220             raise obnamlib.Error('No --repository setting. '
00221                                   'You need to specify it on the command '
00222                                   'line or a configuration file.')
00223         
00224         # This is ugly, but avoids having to update the dependency on
00225         # ttystatus yet again.
00226         if not hasattr(self.app.ts, 'flush'):
00227             self.app.ts.flush = lambda: None
00228 
00229         self.started = time.time()
00230         self.configure_ttystatus_for_backup()
00231         self.app.ts['what'] = 'setting up'
00232         self.app.ts.flush()
00233 
00234         self.compile_exclusion_patterns()
00235         self.memory_dump_counter = 0
00236 
00237         client_name = self.app.settings['client-name']
00238         if self.pretend:
00239             self.repo = self.app.open_repository()
00240             self.repo.open_client(client_name)
00241         else:
00242             self.repo = self.app.open_repository(create=True)
00243             self.add_client(client_name)
00244             self.repo.lock_client(client_name)
00245             
00246             # Need to lock the shared stuff briefly, so encryption etc
00247             # gets initialized.
00248             self.repo.lock_shared()
00249             self.repo.unlock_shared()
00250 
00251         self.errors = False
00252         self.chunkid_pool = ChunkidPool()
00253         try:
00254             if not self.pretend:
00255                 self.repo.start_generation()
00256             self.fs = None
00257             roots = self.app.settings['root'] + args
00258             if roots:
00259                 self.backup_roots(roots)
00260             self.app.ts['what'] = 'committing changes'
00261             self.app.ts.flush()
00262             if not self.pretend:
00263                 self.repo.lock_shared()
00264                 self.add_chunks_to_shared()
00265                 self.repo.commit_client()
00266                 self.repo.commit_shared()
00267             self.repo.fs.close()
00268             self.app.ts.clear()
00269             self.report_stats()
00270 
00271             logging.info('Backup finished.')
00272             self.app.dump_memory_profile('at end of backup run')
00273         except BaseException, e:
00274             logging.debug('Handling exception %s' % str(e))
00275             logging.debug(traceback.format_exc())
00276             self.unlock_when_error()
00277             raise
00278 
00279         if self.errors:
00280             raise obnamlib.Error('There were errors during the backup')

Here is the caller graph for this function:

Back up the list of files in a directory.

Definition at line 617 of file backup_plugin.py.

00617 
00618     def backup_dir_contents(self, root):
00619         '''Back up the list of files in a directory.'''
00620 
00621         tracing.trace('backup_dir: %s', root)
00622         if self.pretend:
00623             return
00624 
00625         new_basenames = self.fs.listdir(root)
00626         old_basenames = self.repo.listdir(self.repo.new_generation, root)
00627 
00628         for old in old_basenames:
00629             pathname = os.path.join(root, old)
00630             if old not in new_basenames:
00631                 self.repo.remove(pathname)
00632         # Files that are created after the previous generation will be
00633         # added to the directory when they are backed up, so we don't
00634         # need to worry about them here.

Here is the call graph for this function:

Back up a chunk of data by putting it into the repository.

Definition at line 570 of file backup_plugin.py.

00570 
00571     def backup_file_chunk(self, data):
00572         '''Back up a chunk of data by putting it into the repository.'''
00573 
00574         def find():
00575             return (self.repo.find_chunks(checksum) + 
00576                      self.chunkid_pool.get(checksum))
00577 
00578         def get(chunkid):
00579             return self.repo.get_chunk(chunkid)
00580 
00581         def put():
00582             self.update_progress_with_upload(len(data))
00583             return self.repo.put_chunk_only(data)
00584             
00585         def share(chunkid):
00586             self.chunkid_pool.add(chunkid, checksum)
00587 
00588         checksum = self.repo.checksum(data)
00589 
00590         mode = self.app.settings['deduplicate']
00591         if mode == 'never':
00592             return put()
00593         elif mode == 'verify':
00594             for chunkid in find():
00595                 data2 = get(chunkid)
00596                 if data == data2:
00597                     return chunkid
00598             else:
00599                 chunkid = put()
00600                 share(chunkid)
00601                 return chunkid
00602         elif mode == 'fatalist':
00603             existing = find()
00604             if existing:
00605                 return existing[0]
00606             else:
00607                 chunkid = put()
00608                 share(chunkid)
00609                 return chunkid
00610         else:
00611             if not hasattr(self, 'bad_deduplicate_reported'):
00612                 logging.error('unknown --deduplicate setting value')
00613                 self.bad_deduplicate_reported = True
00614             chunkid = put()
00615             share(chunkid)
00616             return chunkid

Here is the call graph for this function:

Here is the caller graph for this function:

def obnamlib.plugins.backup_plugin.BackupPlugin.backup_file_contents (   self,
  filename,
  metadata 
)
Back up contents of a regular file.

Definition at line 505 of file backup_plugin.py.

00505 
00506     def backup_file_contents(self, filename, metadata):
00507         '''Back up contents of a regular file.'''
00508         tracing.trace('backup_file_contents: %s', filename)
00509         if self.pretend:
00510             tracing.trace('pretending to upload the whole file')
00511             self.update_progress_with_upload(metadata.st_size)
00512             return
00513 
00514         tracing.trace('setting file chunks to empty')
00515         if not self.pretend:
00516             self.repo.set_file_chunks(filename, [])
00517 
00518         tracing.trace('opening file for reading')
00519         f = self.fs.open(filename, 'r')
00520 
00521         summer = self.repo.new_checksummer()
00522 
00523         max_intree = self.app.settings['node-size'] / 4
00524         if (metadata.st_size <= max_intree and 
00525             self.app.settings['small-files-in-btree']):
00526             contents = f.read()
00527             assert len(contents) <= max_intree # FIXME: silly error checking
00528             f.close()
00529             self.repo.set_file_data(filename, contents)
00530             summer.update(contents)
00531             return summer.digest()
00532 
00533         chunk_size = int(self.app.settings['chunk-size'])
00534         chunkids = []
00535         while True:
00536             tracing.trace('reading some data')
00537             data = f.read(chunk_size)
00538             if not data:
00539                 tracing.trace('end of data')
00540                 break
00541             tracing.trace('got %d bytes of data' % len(data))
00542             summer.update(data)
00543             if not self.pretend:
00544                 chunkids.append(self.backup_file_chunk(data))
00545                 if len(chunkids) >= self.app.settings['chunkids-per-group']:
00546                     tracing.trace('adding %d chunkids to file' % len(chunkids))
00547                     self.repo.append_file_chunks(filename, chunkids)
00548                     self.app.dump_memory_profile('after appending some '
00549                                                     'chunkids')
00550                     chunkids = []
00551             else:
00552                 self.update_progress_with_upload(len(data))
00553             
00554             if not self.pretend and self.time_for_checkpoint():
00555                 logging.debug('making checkpoint in the middle of a file')
00556                 self.repo.append_file_chunks(filename, chunkids)
00557                 chunkids = []
00558                 self.make_checkpoint()
00559             
00560         tracing.trace('closing file')
00561         f.close()
00562         if chunkids:
00563             assert not self.pretend
00564             tracing.trace('adding final %d chunkids to file' % len(chunkids))
00565             self.repo.append_file_chunks(filename, chunkids)
00566         self.app.dump_memory_profile('at end of file content backup for %s' %
00567                                      filename)
00568         tracing.trace('done backing up file contents')
00569         return summer.digest()
        

Here is the call graph for this function:

def obnamlib.plugins.backup_plugin.BackupPlugin.backup_metadata (   self,
  pathname,
  metadata 
)
Back up metadata for a filesystem object

Definition at line 498 of file backup_plugin.py.

00498 
00499     def backup_metadata(self, pathname, metadata):
00500         '''Back up metadata for a filesystem object'''
00501         
00502         tracing.trace('backup_metadata: %s', pathname)
00503         if not self.pretend:
00504             self.repo.create(pathname, metadata)

Here is the call graph for this function:

Back up parents of root, non-recursively.

Definition at line 485 of file backup_plugin.py.

00485 
00486     def backup_parents(self, root):
00487         '''Back up parents of root, non-recursively.'''
00488         root = self.fs.abspath(root)
00489         tracing.trace('backing up parents of %s', root)
00490         while True:
00491             parent = os.path.dirname(root)
00492             metadata = obnamlib.read_metadata(self.fs, root)
00493             if not self.pretend:
00494                 self.repo.create(root, metadata)
00495             if root == parent:
00496                 break
00497             root = parent

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 328 of file backup_plugin.py.

00328 
00329     def backup_roots(self, roots):
00330         self.fs = self.app.fsf.new(roots[0])
00331         self.fs.connect()
00332 
00333         absroots = []
00334         for root in roots:
00335             self.fs.reinit(root)
00336             absroots.append(self.fs.abspath('.'))
00337         
00338         if not self.pretend:
00339             self.remove_old_roots(absroots)
00340 
00341         self.checkpoints = []
00342         self.last_checkpoint = 0
00343         self.interval = self.app.settings['checkpoint']
00344 
00345         for root in roots:
00346             logging.info('Backing up root %s' % root)
00347             self.fs.reinit(root)
00348             absroot = self.fs.abspath('.')
00349             self.root_metadata = self.fs.lstat(absroot)
00350             for pathname, metadata in self.find_files(absroot):
00351                 logging.debug('Backing up %s' % pathname)
00352                 try:
00353                     if stat.S_ISDIR(metadata.st_mode):
00354                         self.backup_dir_contents(pathname)
00355                     elif stat.S_ISREG(metadata.st_mode):
00356                         assert metadata.md5 is None
00357                         metadata.md5 = self.backup_file_contents(pathname,
00358                                                                  metadata)
00359                     self.backup_metadata(pathname, metadata)
00360                 except (IOError, OSError), e:
00361                     msg = 'Can\'t back up %s: %s' % (pathname, e.strerror)
00362                     self.error(msg, e)
00363                     if e.errno == errno.ENOSPC:
00364                         raise
00365                 if self.time_for_checkpoint():
00366                     self.make_checkpoint()
00367 
00368             self.backup_parents('.')
00369 
00370         remove_checkpoints = (not self.errors and
00371                               not self.app.settings['leave-checkpoints']
00372                               and not self.pretend)
00373         if remove_checkpoints:
00374             self.configure_ttystatus_for_checkpoint_removal()
00375             for gen in self.checkpoints:
00376                 self.app.ts['checkpoint'] = gen
00377                 self.repo.remove_generation(gen)
00378 
00379         if self.fs:
00380             self.fs.close()

Here is the call graph for this function:

def obnamlib.plugins.backup_plugin.BackupPlugin.can_be_backed_up (   self,
  pathname,
  st 
)

Definition at line 429 of file backup_plugin.py.

00429 
00430     def can_be_backed_up(self, pathname, st):
00431         if self.app.settings['one-file-system']:
00432             if st.st_dev != self.root_metadata.st_dev: 
00433                 logging.info('Excluding (one-file-system): %s' %
00434                              pathname)
00435                 return False
00436 
00437         for pat in self.exclude_pats:
00438             if pat.search(pathname):
00439                 logging.info('Excluding (pattern): %s' % pathname)
00440                 return False
00441 
00442         if stat.S_ISDIR(st.st_mode) and self.app.settings['exclude-caches']:
00443             tag_filename = 'CACHEDIR.TAG'
00444             tag_contents = 'Signature: 8a477f597d28d172789f06886806bc55'
00445             tag_path = os.path.join(pathname, 'CACHEDIR.TAG')
00446             if self.fs.exists(tag_path):
00447                 # Can't use with, because Paramiko's SFTPFile does not work.
00448                 f = self.fs.open(tag_path, 'rb')
00449                 data = f.read(len(tag_contents))
00450                 f.close()
00451                 if data == tag_contents:
00452                     logging.info('Excluding (cache dir): %s' % pathname)
00453                     return False
00454         
00455         return True

Here is the caller graph for this function:

Definition at line 311 of file backup_plugin.py.

00311 
00312     def compile_exclusion_patterns(self):
00313         log = self.app.settings['log']
00314         if log:
00315             log = self.app.settings['log']
00316             self.app.settings['exclude'].append(log)
00317         for pattern in self.app.settings['exclude']:
00318             logging.debug('Exclude pattern: %s' % pattern)
00319 
00320         self.exclude_pats = []
00321         for x in self.app.settings['exclude']:
00322             try:
00323                 self.exclude_pats.append(re.compile(x))
00324             except re.error, e:
00325                 msg = 'error compiling regular expression "%s": %s' % (x, e)
00326                 logging.error(msg)
00327                 self.app.ts.error(msg)

Definition at line 112 of file backup_plugin.py.

00112 
00113     def configure_ttystatus_for_backup(self):
00114         self.app.ts['current-file'] = ''
00115         self.app.ts['uploaded-bytes'] = 0
00116         self.file_count = 0
00117         self.uploaded_bytes = 0
00118 
00119         self.app.ts.format('%ElapsedTime() '
00120                            '%Counter(current-file) '
00121                            'files; '
00122                            '%ByteSize(uploaded-bytes) '
00123                            '('
00124                            '%ByteSpeed(uploaded-bytes,10)'
00125                            ') '
00126                            '%String(what)')

Definition at line 127 of file backup_plugin.py.

00127 
00128     def configure_ttystatus_for_checkpoint_removal(self):
00129         self.app.ts['what'] = 'removing checkpoints'

Definition at line 60 of file backup_plugin.py.

00060 
00061     def enable(self):
00062         backup_group = obnamlib.option_group['backup'] = 'Backing up'
00063         perf_group = obnamlib.option_group['perf']
00064     
00065         self.app.add_subcommand('backup', self.backup,
00066                                 arg_synopsis='[FILE]...')
00067         self.app.settings.string_list(['root'], 'what to backup')
00068         self.app.settings.string_list(['exclude'], 
00069                                  'regular expression for pathnames to '
00070                                  'exclude from backup (can be used multiple '
00071                                  'times)',
00072                                  group=backup_group)
00073         self.app.settings.boolean(['exclude-caches'],
00074                                     'exclude directories (and their subdirs) '
00075                                     'that contain a CACHEDIR.TAG file',
00076                                  group=backup_group)
00077         self.app.settings.boolean(['one-file-system'],
00078                                     'exclude directories (and their subdirs) '
00079                                     'that are in a different filesystem',
00080                                  group=backup_group)
00081         self.app.settings.bytesize(['checkpoint'],
00082                                       'make a checkpoint after a given SIZE '
00083                                       '(%default)',
00084                                     metavar='SIZE',
00085                                     default=1024**3,
00086                                  group=backup_group)
00087         self.app.settings.integer(['chunkids-per-group'],
00088                                   'encode NUM chunk ids per group (%default)',
00089                                   metavar='NUM',
00090                                   default=obnamlib.DEFAULT_CHUNKIDS_PER_GROUP,
00091                                   group=perf_group)
00092         self.app.settings.choice(['deduplicate'],
00093                                  ['fatalist', 'never', 'verify'],
00094                                  'find duplicate data in backed up data '
00095                                     'and store it only once; three modes '
00096                                     'are available: never de-duplicate, '
00097                                     'verify that no hash collisions happen, '
00098                                     'or (the default) fatalistically accept '
00099                                     'the risk of collisions',
00100                                  metavar='MODE',
00101                                  group=backup_group)
00102         self.app.settings.boolean(['leave-checkpoints'],
00103                                   'leave checkpoint generations at the end '
00104                                     'of a successful backup run',
00105                                  group=backup_group)
00106         self.app.settings.boolean(['small-files-in-btree'],
00107                                   'put contents of small files directly into '
00108                                     'the per-client B-tree, instead of '
00109                                     'separate chunk files; do not use this '
00110                                     'as it is quite bad for performance',
00111                                  group=backup_group)

Here is the call graph for this function:

def obnamlib.plugins.backup_plugin.BackupPlugin.error (   self,
  msg,
  exc = None 
)

Definition at line 194 of file backup_plugin.py.

00194 
00195     def error(self, msg, exc=None):
00196         self.errors = True
00197         logging.error(msg)
00198         if exc:
00199             logging.debug(repr(exc))

Here is the caller graph for this function:

Find all files and directories that need to be backed up.

This is a generator. It yields (pathname, metadata) pairs.

The caller should not recurse through directories, just backup
the directory itself (name, metadata, file list).

Definition at line 403 of file backup_plugin.py.

00403 
00404     def find_files(self, root):
00405         '''Find all files and directories that need to be backed up.
00406         
00407         This is a generator. It yields (pathname, metadata) pairs.
00408         
00409         The caller should not recurse through directories, just backup
00410         the directory itself (name, metadata, file list).
00411         
00412         '''
00413 
00414         for pathname, st in self.fs.scan_tree(root, ok=self.can_be_backed_up):
00415             tracing.trace('considering %s' % pathname)
00416             try:
00417                 metadata = obnamlib.read_metadata(self.fs, pathname, st=st)
00418                 self.update_progress_with_file(pathname, metadata)
00419                 if self.needs_backup(pathname, metadata):
00420                     yield pathname, metadata
00421             except GeneratorExit:
00422                 raise
00423             except KeyboardInterrupt:
00424                 logging.error('Keyboard interrupt')
00425                 raise
00426             except BaseException, e:
00427                 msg = 'Cannot back up %s: %s' % (pathname, str(e))
00428                 self.error(msg, e)

Here is the call graph for this function:

Definition at line 385 of file backup_plugin.py.

00385 
00386     def make_checkpoint(self):
00387         logging.info('Making checkpoint')
00388         self.app.ts['what'] = 'making checkpoint'
00389         self.app.ts.flush()
00390         if not self.pretend:
00391             self.checkpoints.append(self.repo.new_generation)
00392             self.backup_parents('.')
00393             self.repo.lock_shared()
00394             self.add_chunks_to_shared()
00395             self.repo.commit_client(checkpoint=True)
00396             self.repo.commit_shared()
00397             self.last_checkpoint = self.repo.fs.bytes_written
00398             self.repo = self.app.open_repository(repofs=self.repo.fs.fs)
00399             self.repo.lock_client(self.app.settings['client-name'])
00400             self.repo.start_generation()
00401             self.app.dump_memory_profile('at end of checkpoint')
00402         self.app.ts['what'] = self.app.ts['current-file']

Here is the call graph for this function:

Here is the caller graph for this function:

def obnamlib.plugins.backup_plugin.BackupPlugin.needs_backup (   self,
  pathname,
  current 
)
Does a given file need to be backed up?

Definition at line 456 of file backup_plugin.py.

00456 
00457     def needs_backup(self, pathname, current):
00458         '''Does a given file need to be backed up?'''
00459         
00460         # Directories always require backing up so that backup_dir_contents
00461         # can remove stuff that no longer exists from them.
00462         if current.isdir():
00463             return True
00464         if self.pretend:
00465             gens = self.repo.list_generations()
00466             if not gens:
00467                 return True
00468             gen = gens[-1]
00469         else:
00470             gen = self.repo.new_generation
00471         try:
00472             old = self.repo.get_metadata(gen, pathname)
00473         except obnamlib.Error:
00474             # File does not exist in the previous generation, so it
00475             # does need to be backed up.
00476             return True
00477         return (current.st_mtime_sec != old.st_mtime_sec or
00478                 current.st_mtime_nsec != old.st_mtime_nsec or
00479                 current.st_mode != old.st_mode or
00480                 current.st_nlink != old.st_nlink or
00481                 current.st_size != old.st_size or
00482                 current.st_uid != old.st_uid or
00483                 current.st_gid != old.st_gid or
00484                 current.xattr != old.xattr)

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 200 of file backup_plugin.py.

00200 
00201     def parse_checkpoint_size(self, value):
00202         p = obnamlib.ByteSizeParser()
00203         p.set_default_unit('MiB')
00204         return p.parse(value)
        

Definition at line 206 of file backup_plugin.py.

00206 
00207     def pretend(self):
00208         return self.app.settings['pretend']

Here is the caller graph for this function:

Remove from started generation anything that is not a backup root.

We recurse from filesystem root directory until getting to one of 
the new backup roots, or a directory or file that is not a parent 
of one of the new backup roots. We remove anything that is not a
new backup root, or their parent.

Definition at line 635 of file backup_plugin.py.

00635 
00636     def remove_old_roots(self, new_roots):
00637         '''Remove from started generation anything that is not a backup root.
00638         
00639         We recurse from filesystem root directory until getting to one of 
00640         the new backup roots, or a directory or file that is not a parent 
00641         of one of the new backup roots. We remove anything that is not a
00642         new backup root, or their parent.
00643         
00644         '''
00645         
00646         def is_parent(pathname):
00647             x = pathname + os.sep
00648             for new_root in new_roots:
00649                 if new_root.startswith(x):
00650                     return True
00651             return False
00652 
00653         def helper(dirname):
00654             gen_id = self.repo.new_generation
00655             basenames = self.repo.listdir(gen_id, dirname)
00656             for basename in basenames:
00657                 pathname = os.path.join(dirname, basename)
00658                 if is_parent(pathname):
00659                     metadata = self.repo.get_metadata(gen_id, pathname)
00660                     if metadata.isdir():
00661                         helper(pathname)
00662                 elif pathname not in new_roots:
00663                     self.repo.remove(pathname)
00664 
00665         assert not self.pretend
00666         helper('/')
00667 

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 139 of file backup_plugin.py.

00139 
00140     def report_stats(self):
00141         size_table = [
00142             (1024**4, 'TiB'),
00143             (1024**3, 'GiB'),
00144             (1024**2, 'MiB'),
00145             (1024**1, 'KiB'),
00146             (0, 'B')
00147         ]
00148         
00149         for size_base, size_unit in size_table:
00150             if self.uploaded_bytes >= size_base:
00151                 if size_base > 0:
00152                     size_amount = float(self.uploaded_bytes) / float(size_base)
00153                 else:
00154                     size_amount = float(self.uploaded_bytes)
00155                 break
00156 
00157         speed_table = [
00158             (1024**3, 'GiB/s'),
00159             (1024**2, 'MiB/s'),
00160             (1024**1, 'KiB/s'),
00161             (0, 'B/s')
00162         ]
00163         duration = time.time() - self.started
00164         speed = float(self.uploaded_bytes) / duration
00165         for speed_base, speed_unit in speed_table:
00166             if speed >= speed_base:
00167                 if speed_base > 0:
00168                     speed_amount = speed / speed_base
00169                 else:
00170                     speed_amount = speed
00171                 break
00172 
00173         duration_string = ''
00174         seconds = duration
00175         if seconds >= 3600:
00176             duration_string += '%dh' % int(seconds/3600)
00177             seconds %= 3600
00178         if seconds >= 60:
00179             duration_string += '%dm' % int(seconds/60)
00180             seconds %= 60
00181         if seconds > 0:
00182             duration_string += '%ds' % round(seconds)
00183 
00184         logging.info('Backup performance statistics:')
00185         logging.info('* files found: %s' % self.file_count)
00186         logging.info('* uploaded data: %s bytes (%s %s)' % 
00187                         (self.uploaded_bytes, size_amount, size_unit))
00188         logging.info('* duration: %s s' % duration)
00189         logging.info('* average speed: %s %s' % (speed_amount, speed_unit))
00190         self.app.ts.notify('Backed up %d files, uploaded %.1f %s '
00191                            'in %s at %.1f %s average speed' %
00192                             (self.file_count, size_amount, size_unit,
00193                              duration_string, speed_amount, speed_unit))

Definition at line 381 of file backup_plugin.py.

00381 
00382     def time_for_checkpoint(self):
00383         bytes_since = (self.repo.fs.bytes_written - self.last_checkpoint)
00384         return bytes_since >= self.interval

Here is the caller graph for this function:

Definition at line 281 of file backup_plugin.py.

00281 
00282     def unlock_when_error(self):
00283         try:
00284             if self.repo.got_client_lock:
00285                 logging.info('Unlocking client because of error')
00286                 self.repo.unlock_client()
00287             if self.repo.got_shared_lock:
00288                 logging.info('Unlocking shared trees because of error')
00289                 self.repo.unlock_shared()
00290         except BaseException, e2:
00291             logging.debug('Got second exception while unlocking: %s' % 
00292                             str(e2))
00293             logging.debug(traceback.format_exc())

def obnamlib.plugins.backup_plugin.BackupPlugin.update_progress_with_file (   self,
  filename,
  metadata 
)

Definition at line 130 of file backup_plugin.py.

00130 
00131     def update_progress_with_file(self, filename, metadata):
00132         self.app.ts['what'] = filename
00133         self.app.ts['current-file'] = filename
00134         self.file_count += 1

Here is the caller graph for this function:

Definition at line 135 of file backup_plugin.py.

00135 
00136     def update_progress_with_upload(self, amount):
00137         self.app.ts['uploaded-bytes'] += amount
00138         self.uploaded_bytes += amount

Here is the caller graph for this function:


Member Data Documentation

Definition at line 612 of file backup_plugin.py.

Definition at line 340 of file backup_plugin.py.

Definition at line 251 of file backup_plugin.py.

Definition at line 195 of file backup_plugin.py.

Definition at line 319 of file backup_plugin.py.

Definition at line 115 of file backup_plugin.py.

Definition at line 255 of file backup_plugin.py.

Definition at line 342 of file backup_plugin.py.

Definition at line 341 of file backup_plugin.py.

Definition at line 234 of file backup_plugin.py.

Definition at line 238 of file backup_plugin.py.

Definition at line 348 of file backup_plugin.py.

Definition at line 228 of file backup_plugin.py.

Definition at line 116 of file backup_plugin.py.


The documentation for this class was generated from the following file: