def main(args=None): '''Print file system statistics to sys.stdout''' if args is None: args = sys.argv[1:] options = parse_args(args) setup_logging(options) ctrlfile = assert_fs_owner(options.mountpoint, mountpoint=True) # Use a decent sized buffer, otherwise the statistics have to be # calculated thee(!) times because we need to invoce getxattr # three times. buf = llfuse.getxattr(ctrlfile, 's3qlstat', size_guess=256) (entries, blocks, inodes, fs_size, dedup_size, compr_size, db_size, cache_used, cache_dirty) = struct.unpack('QQQQQQQQQ', buf) p_dedup = dedup_size * 100 / fs_size if fs_size else 0 p_compr_1 = compr_size * 100 / fs_size if fs_size else 0 p_compr_2 = compr_size * 100 / dedup_size if dedup_size else 0 print ('Directory entries: %d' % entries, 'Inodes: %d' % inodes, 'Data blocks: %d' % blocks, 'Total data size: %s' % pretty_print_size(fs_size), 'After de-duplication: %s (%.2f%% of total)' % (pretty_print_size(dedup_size), p_dedup), 'After compression: %s (%.2f%% of total, %.2f%% of de-duplicated)' % (pretty_print_size(compr_size), p_compr_1, p_compr_2), 'Database size: %s (uncompressed)' % pretty_print_size(db_size), 'Cache usage: %s (dirty: %s)' % (pretty_print_size(cache_used), pretty_print_size(cache_dirty)), sep='\n')
def blocking_umount(mountpoint): '''Invoke fusermount and wait for daemon to terminate.''' with open('/dev/null', 'wb') as devnull: if subprocess.call(['fuser', '-m', mountpoint], stdout=devnull, stderr=devnull) == 0: raise MountInUseError(mountpoint) ctrlfile = os.path.join(mountpoint, CTRL_NAME) log.debug('Flushing cache...') llfuse.setxattr(ctrlfile, 's3ql_flushcache!', b'dummy') # Get pid log.debug('Trying to get pid') pid = parse_literal(llfuse.getxattr(ctrlfile, 's3ql_pid?'), int) log.debug('PID is %d', pid) # Get command line to make race conditions less-likely cmdline = get_cmdline(pid) # Unmount log.debug('Unmounting...') if os.getuid() == 0 or platform.system() == 'Darwin': # MacOS X always uses umount rather than fusermount umount_cmd = ['umount', mountpoint] else: umount_cmd = ['fusermount', '-u', mountpoint] if subprocess.call(umount_cmd) != 0: raise UmountSubError(mountpoint) # Wait for daemon log.debug('Uploading metadata...') step = 0.1 while True: try: os.kill(pid, 0) except OSError: log.debug('Kill failed, assuming daemon has quit.') break # Check that the process did not terminate and the PID # was reused by a different process cmdline2 = get_cmdline(pid) if cmdline2 is None: log.debug('Reading cmdline failed, assuming daemon has quit.') break elif cmdline2 == cmdline: log.debug('PID still alive and commandline unchanged.') else: log.debug('PID still alive, but cmdline changed') break # Process still exists, we wait log.debug('Daemon seems to be alive, waiting...') time.sleep(step) if step < 1: step += 0.1
def main(args=None): '''Print file system statistics to sys.stdout''' if args is None: args = sys.argv[1:] options = parse_args(args) setup_logging(options) ctrlfile = assert_fs_owner(options.mountpoint, mountpoint=True) # Use a decent sized buffer, otherwise the statistics have to be # calculated thee(!) times because we need to invoce getxattr # three times. buf = llfuse.getxattr(ctrlfile, 's3qlstat', size_guess=256) (entries, blocks, inodes, fs_size, dedup_size, compr_size, db_size) = struct.unpack('QQQQQQQ', buf) p_dedup = dedup_size * 100 / fs_size if fs_size else 0 p_compr_1 = compr_size * 100 / fs_size if fs_size else 0 p_compr_2 = compr_size * 100 / dedup_size if dedup_size else 0 mb = 1024**2 print( 'Directory entries: %d' % entries, 'Inodes: %d' % inodes, 'Data blocks: %d' % blocks, 'Total data size: %.2f MiB' % (fs_size / mb), 'After de-duplication: %.2f MiB (%.2f%% of total)' % (dedup_size / mb, p_dedup), 'After compression: %.2f MiB (%.2f%% of total, %.2f%% of de-duplicated)' % (compr_size / mb, p_compr_1, p_compr_2), 'Database size: %.2f MiB (uncompressed)' % (db_size / mb), '(some values do not take into account not-yet-uploaded dirty blocks in cache)', sep='\n')
def main(args=None): '''Print file system statistics to sys.stdout''' if args is None: args = sys.argv[1:] options = parse_args(args) setup_logging(options) ctrlfile = assert_fs_owner(options.mountpoint, mountpoint=True) # Use a decent sized buffer, otherwise the statistics have to be # calculated thee(!) times because we need to invoce getxattr # three times. buf = llfuse.getxattr(ctrlfile, 's3qlstat', size_guess=256) (entries, blocks, inodes, fs_size, dedup_size, compr_size, db_size) = struct.unpack('QQQQQQQ', buf) p_dedup = dedup_size * 100 / fs_size if fs_size else 0 p_compr_1 = compr_size * 100 / fs_size if fs_size else 0 p_compr_2 = compr_size * 100 / dedup_size if dedup_size else 0 mb = 1024 ** 2 print ('Directory entries: %d' % entries, 'Inodes: %d' % inodes, 'Data blocks: %d' % blocks, 'Total data size: %.2f MiB' % (fs_size / mb), 'After de-duplication: %.2f MiB (%.2f%% of total)' % (dedup_size / mb, p_dedup), 'After compression: %.2f MiB (%.2f%% of total, %.2f%% of de-duplicated)' % (compr_size / mb, p_compr_1, p_compr_2), 'Database size: %.2f MiB (uncompressed)' % (db_size / mb), '(some values do not take into account not-yet-uploaded dirty blocks in cache)', sep='\n')
def main(args=None): '''Print file system statistics to sys.stdout''' if args is None: args = sys.argv[1:] options = parse_args(args) setup_logging(options) mountpoint = options.mountpoint # Check if it's a mount point if not posixpath.ismount(mountpoint): raise QuietError('%s is not a mount point' % mountpoint) # Check if it's an S3QL mountpoint ctrlfile = os.path.join(mountpoint, CTRL_NAME) if not (CTRL_NAME not in llfuse.listdir(mountpoint) and os.path.exists(ctrlfile)): raise QuietError('%s is not a mount point' % mountpoint) if os.stat(ctrlfile).st_uid != os.geteuid() and os.geteuid() != 0: raise QuietError('Only root and the mounting user may run s3qlstat.') # Use a decent sized buffer, otherwise the statistics have to be # calculated thee(!) times because we need to invoce getxattr # three times. buf = llfuse.getxattr(ctrlfile, b's3qlstat', size_guess=256) (entries, blocks, inodes, fs_size, dedup_size, compr_size, db_size) = struct.unpack('QQQQQQQ', buf) p_dedup = dedup_size * 100 / fs_size if fs_size else 0 p_compr_1 = compr_size * 100 / fs_size if fs_size else 0 p_compr_2 = compr_size * 100 / dedup_size if dedup_size else 0 mb = 1024 ** 2 print ('Directory entries: %d' % entries, 'Inodes: %d' % inodes, 'Data blocks: %d' % blocks, 'Total data size: %.2f MiB' % (fs_size / mb), 'After de-duplication: %.2f MiB (%.2f%% of total)' % (dedup_size / mb, p_dedup), 'After compression: %.2f MiB (%.2f%% of total, %.2f%% of de-duplicated)' % (compr_size / mb, p_compr_1, p_compr_2), 'Database size: %.2f MiB (uncompressed)' % (db_size / mb), '(some values do not take into account not-yet-uploaded dirty blocks in cache)', sep='\n')
def _getxattr_helper(path, name): try: value = llfuse.getxattr(path, name) except OSError as exc: errno = exc.errno value = None if not hasattr(os, 'getxattr'): return value try: value2 = os.getxattr(path, name) except OSError as exc: assert exc.errno == errno else: assert value2 is not None assert value2 == value return value
def blocking_umount(mountpoint): '''Invoke fusermount and wait for daemon to terminate.''' devnull = open('/dev/null', 'wb') if subprocess.call( ['fuser', '-m', mountpoint], stdout=devnull, stderr=devnull ) == 0: raise MountInUseError(mountpoint) ctrlfile = os.path.join(mountpoint, CTRL_NAME) log.debug('Flushing cache...') llfuse.setxattr(ctrlfile, b's3ql_flushcache!', b'dummy') # Get pid log.debug('Trying to get pid') pid = int(llfuse.getxattr(ctrlfile, b's3ql_pid?')) log.debug('PID is %d', pid) # Get command line to make race conditions less-likely with open('/proc/%d/cmdline' % pid, 'r') as fh: cmdline = fh.readline() log.debug('cmdline is %r', cmdline) # Unmount log.debug('Unmounting...') # This seems to be necessary to prevent weird busy errors time.sleep(3) if os.getuid() == 0: umount_cmd = ['umount', mountpoint] else: umount_cmd = ['fusermount', '-u', mountpoint] if subprocess.call(umount_cmd) != 0: raise UmountError(mountpoint) # Wait for daemon log.debug('Uploading metadata...') step = 0.5 while True: try: os.kill(pid, 0) except OSError: log.debug('Kill failed, assuming daemon has quit.') break # Check that the process did not terminate and the PID # was reused by a different process try: with open('/proc/%d/cmdline' % pid, 'r') as fh: if fh.readline() != cmdline: log.debug('PID still alive, but cmdline changed') # PID must have been reused, original process terminated break else: log.debug('PID still alive and commandline unchanged.') except OSError: # Process must have exited by now log.debug('Reading cmdline failed, assuming daemon has quit.') break # Process still exists, we wait log.debug('Daemon seems to be alive, waiting...') time.sleep(step) if step < 10: step *= 2