#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import (
absolute_import, division, print_function, unicode_literals
)
from nine import (
IS_PYTHON2, basestring, chr, class_types, filter, integer_types,
implements_iterator, implements_to_string, implements_repr,
input, iterkeys, iteritems, itervalues, long, map,
native_str, nine, nimport, range, range_list, reraise, str, zip
)
argparse = nimport('argparse')
glob = nimport('glob')
grp = nimport('grp')
os = nimport('os')
pwd = nimport('pwd')
subprocess = nimport('subprocess')
sys = nimport('sys')
time = nimport('time')
DEFAULT_PREFIX = 'home_'
def run():
"""Runs this program.
The program runs 'apt-clone clone' to store the current package list
into a time-stamped file in the given destination directory.
Returns:
None
"""
start_time = time.time()
options = parse_cmd_line(default_prefix=DEFAULT_PREFIX)
remove_old_files(
pattern='{destdir}{sep}{prefix}*'.format(
destdir=options['destdir'],
sep=os.sep,
prefix=options['prefix']),
remove=options['remove'],
verbose=options['verbose'])
dest_prefix = generate_results_file_prefix(
dest_dir=options['destdir'],
prefix=options['prefix'])
apt_clone_clone(
dest_prefix=dest_prefix,
verbose=options['verbose'])
change_file_owner_group(
pattern='{}*apt-clone*'.format(dest_prefix),
user=options['user'],
group=options['user'],
verbose=options['verbose'])
if options['verbose']:
print('Done, in {} seconds!'.format(time.time() - start_time))
def parse_cmd_line(default_prefix):
"""Parses the command-line arguments.
Arguments:
default_prefix: the default prefix for current package list
Returns:
A dictionary with each of the supplied command-line arguments.
"""
parser = argparse.ArgumentParser(
description="Stores apt-clone clone results")
parser.add_argument(
'user',
help=' '.join([
'specify the name of the owner of the apt-clone clone results file;',
'the results files user and group will be set to this value']))
parser.add_argument(
'destdir',
help=' '.join([
'specify the directory that the new results file',
'will be saved into']))
parser.add_argument(
'--prefix', '-p',
default=default_prefix,
help=' '.join([
'specify the prefix to use for the newly created results file',
"(default: '%(default)s')"]))
parser.add_argument(
'--remove', '-r',
action='store_true',
default=False,
help='specify this to have obsolete results files removed')
parser.add_argument(
'--verbose', '-v',
action='store_true',
default=False,
help='specify this to display verbose output')
# vars() turns Namespace into a regular dictionary
options = vars(parser.parse_args())
options['destdir'] = chomp_sep(options['destdir'])
return options
def chomp_sep(dir_name):
"""Removes any trailing directory separator characters from the given
directory name.
Arguments:
dir_name: the name that has to have any trailing slashes removed
Returns:
The directory name with no trailing separator characters
"""
while dir_name.endswith(os.sep):
dir_name = dir_name[:-1]
return dir_name
def generate_results_file_prefix(dest_dir, prefix):
"""Generates a results file filename prefix.
Arguments:
dest_dir: where the results file should be saved
prefix: the prefix to use for the results file
Returns:
A results file filename prefix of the form:
/
where the year, month, day, etc. values represent the local time
"""
localtime = time.localtime()
return ('{dest_dir}{sep}{prefix}{year:04d}{month:02d}{day:02d}' +
'{hour:02d}{minute:02d}{second:02d}').format(
dest_dir=dest_dir,
prefix=prefix,
year=localtime[0],
month=localtime[1],
day=localtime[2],
hour=localtime[3],
minute=localtime[4],
second=localtime[5],
sep=os.sep)
def remove_old_files(**kwargs):
"""Only keeps the newest files that match a glob pattern.
Arguments:
kwargs: a dictionary with the following keys:-
'pattern' - the glob pattern of files to remove
'remove' - whether to remove old files
'verbose' - whether to output text describing non-fatal events
Returns:
None
"""
pattern = kwargs.pop('pattern')
remove = kwargs.pop('remove')
verbose= kwargs.pop('verbose')
if kwargs:
raise TypeError('Unexpected **kwargs: %r' % kwargs)
if not remove:
return
for f in glob.glob(pattern):
os.remove(f)
if verbose:
print("Removed file '{filename}'".format(filename=f))
def apt_clone_clone(dest_prefix, verbose):
"""Creates a clone file
Arguments:
dest_prefix: the prefix to use for the apt-clone clone results file
verbose: whether to output text describing non-fatal events
Returns:
None
"""
subprocess.call(
'apt-clone clone "{}" &>/dev/null'.format(dest_prefix),
shell=True)
if verbose:
print('Performed apt-clone clone to: {}'.format(dest_prefix))
def change_file_owner_group(**kwargs):
"""Changes the owner and group of the named file.
Arguments:
kwargs: a dictionary with the following keys:-
'pattern': the glob pattern of files to change ownership of
'user': the new owner to assign to the file
'group': the new group to assign to the file;
this is the same as 'user' if not given
'verbose': whether to output text describing non-fatal events
Returns:
None
"""
pattern = kwargs.pop('pattern')
user = kwargs.pop('user')
group = kwargs.pop('group', user)
verbose= kwargs.pop('verbose')
if kwargs:
raise TypeError('Unexpected **kwargs: %r' % kwargs)
for filename in glob.glob(pattern):
try:
os.chown(filename,
pwd.getpwnam(user).pw_uid,
grp.getgrnam(group).gr_gid)
if verbose:
print(
' '.join(
"Changed ownership of '{filename}'",
"'{user}:{group}'".format(
filename=filename,
user=user,
group=group)))
except os.OSError as err:
print("Unable to change ownership of "
+ "'{filename}' to '{user}:{group}' => {err}".format(
filename=filename,
user=user,
group=group,
err=err),
file=sys.stderr)
if __name__ == '__main__':
run()