33 from __future__
import print_function
40 from distutils.spawn
import find_executable
53 Pretty print cmds, ask if they should be run, and if so, runs
56 :param cmds: a list of commands executed one after another, ``list``
57 :param cwd: (optional) set cwd of command that is executed, ``str``
58 :returns: ``True`` if cmds were run.
62 return '"%s"' % s
if ' ' in s
else s
63 accepted =
_ask(
'\n'.join([
' '.join([quote(s)
for s
in c])
for c
in cmds]))
71 ask user with provided comment. If user responds with y, return True
73 :param comment: comment, ``str``
74 :return: ``True`` if user responds with y
76 sys.stdout.write(
'Okay to perform:\n\n%s\n(y/n)?\n' % comment)
78 input = sys.stdin.readline().strip().lower()
79 if input
in [
'y',
'n']:
86 Runs cmds using subprocess.check_call.
88 :param cmds: a list of commands executed one after another, ``list``
89 :param cwd: (optional) set cwd of command that is executed, ``str``
93 subprocess.check_call(c, cwd=cwd)
95 subprocess.check_call(c)
99 print(
"""Usage: rosclean <command>
102 \trosclean check\tCheck usage of log files
103 \trosclean purge\tRemove log files
105 sys.exit(getattr(os,
'EX_USAGE', 1))
109 home_dir = rospkg.get_ros_home()
110 log_dir = rospkg.get_log_dir()
111 dirs = [(log_dir,
'ROS node logs'),
112 (os.path.join(home_dir,
'rosmake'),
'rosmake logs')]
113 return [x
for x
in dirs
if os.path.isdir(x[0])]
118 for d, label
in dirs:
120 print(
'%s %s' % (desc, label))
125 for dirpath, dirnames, filenames
in os.walk(d):
127 fp = os.path.join(dirpath, f)
128 total_size += os.path.getsize(fp)
134 Get human-readable disk usage for directory
136 :param d: directory path, ``str`
137 :returns: human-readable disk usage (du -h), ``str``
140 if platform.system()
in [
'Linux',
'FreeBSD']:
142 return subprocess.Popen([
'du',
'-sh', d], stdout=subprocess.PIPE).communicate()[0].split()[0].decode()
145 elif platform.system() ==
'Windows':
147 return 'Total Size: ' + str(total_size) +
' ' + d
154 Get disk usage in bytes for directory
155 :param d: directory path, ``str``
156 :returns: disk usage in bytes (du -b) or (du -A) * 1024, ``int``
157 :raises: :exc:`CleanupException` If get_disk_usage() cannot be used on this platform
159 if platform.system() ==
'Windows':
165 du = find_executable(
'du')
167 if platform.system() ==
'Linux':
169 elif platform.system() ==
'FreeBSD':
170 cmd = [du,
'-skA', d]
174 if os.path.basename(os.readlink(du)) ==
'busybox':
184 return int(subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0].split()[0]) * unit
191 Get files and directories in specified path sorted by last modified time
192 :param d: directory path, ```str```
193 :return: a list of files and directories sorted by last modified time (old first), ```list```
195 files = os.listdir(d)
196 files.sort(key=
lambda f: os.path.getmtime(os.path.join(d, f)))
203 for d, label
in dirs:
205 print(
'Purging %s.' % label)
206 if platform.system() ==
'Windows':
207 cmds = [[
'cmd',
'/c',
'rd',
'/s',
'/q', d]]
209 cmds = [[
'rm',
'-rf', d]]
214 print(
'PLEASE BE CAREFUL TO VERIFY THE COMMAND BELOW!')
217 print(
'FAILED to execute command', file=sys.stderr)
221 if log_size <= args.size * 1024 * 1024:
222 print(
'Directory size of %s is %d MB which is already below the requested threshold of %d MB.' % (label, log_size / 1024 / 1024, args.size))
224 print(
'Purging %s until directory size is at most %d MB (currently %d MB).' % (label, args.size, log_size / 1024 / 1024))
226 print(
'PLEASE BE CAREFUL TO VERIFY THE COMMAND BELOW!')
227 if not _ask(
'Purge some of old logs in %s' % d):
230 if log_size <= args.size * 1024 * 1024:
232 path = os.path.join(d, f)
234 if platform.system() ==
'Windows':
235 cmds = [[
'cmd',
'/c',
'rd',
'/s',
'/q', path]]
237 cmds = [[
'rm',
'-rf', path]]
241 print(
'FAILED to execute command', file=sys.stderr)
247 parser = argparse.ArgumentParser(prog=
'rosclean')
248 subparsers = parser.add_subparsers(required=
True, dest=
'{check,purge}')
249 parser_check = subparsers.add_parser(
'check', help=
'Check usage of log files')
250 parser_check.set_defaults(func=_rosclean_cmd_check)
251 parser_purge = subparsers.add_parser(
'purge', help=
'Remove log files')
252 parser_purge.set_defaults(func=_rosclean_cmd_purge)
253 parser_purge.add_argument(
'-y', action=
'store_true', default=
False, help=
'CAUTION: automatically confirms all questions to delete files')
254 parser_purge.add_argument(
'--size', action=
'store', default=
None, type=int, help=
'Maximum total size in MB to keep when deleting old files')
255 args = parser.parse_args(argv[1:])
259 if __name__ ==
'__main__':