[PATCH 2/3] tuna: Allow cpuset destroy to accept multiple names
John Kacur <[email protected]> Thu, 16 Jul 2026 15:10:27 -0400
| Newsgroups | org.kernel.vger.linux-rt-users |
|---|---|
| Message-ID | <[email protected]> |
Changed 'tuna cpuset destroy' to accept multiple cpuset names in a single command, improving usability and consistency with other commands. Previous behavior: tuna cpuset destroy NAME # Single cpuset only tuna cpuset destroy --pattern '*' # Multiple via pattern New behavior: tuna cpuset destroy NAME1 NAME2 NAME3 # Multiple by name tuna cpuset destroy --pattern '*' # Multiple via pattern (unchanged) This change makes the command more flexible and allows users to destroy specific cpusets without needing to use pattern matching. The implementation changed the 'name' argument from nargs='?' to 'names' with nargs='*', and updated the handler function to iterate over the list. Also added parser_class=HelpMessageParser to subparsers to improve error message context (though argparse still shows top-level help for some unrecognized argument errors due to internal behavior). Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: John Kacur <[email protected]> --- tuna-cmd.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/tuna-cmd.py b/tuna-cmd.py index ae02f2d50936..d2ba0238ab28 100755 --- a/tuna-cmd.py +++ b/tuna-cmd.py @@ -149,7 +149,7 @@ def gen_parser(): parser.add_argument('-D', '--debug', **MODS['debug']) parser.add_argument('-w', '--warn', **MODS['warn']) - subparser = parser.add_subparsers(dest='command') + subparser = parser.add_subparsers(dest='command', parser_class=HelpMessageParser) idle_set = subparser.add_parser('cpu_power', description='Manage CPU idle state disabling (requires libcpupower and it\'s Python bindings)', @@ -265,7 +265,7 @@ def gen_parser(): gui.add_argument('-K', '--no_kthreads', **MODS['no_kthreads']) # Cpuset nested subcommands - cpuset_subparser = cpuset.add_subparsers(dest='cpuset_command', required=True) + cpuset_subparser = cpuset.add_subparsers(dest='cpuset_command', required=True, parser_class=HelpMessageParser) # cpuset create cpuset_create = cpuset_subparser.add_parser('create', description='Create a new cpuset', help='Create a new cpuset') @@ -285,7 +285,7 @@ def gen_parser(): # cpuset destroy cpuset_destroy = cpuset_subparser.add_parser('destroy', description='Destroy cpuset(s)', help='Destroy cpuset(s)') - cpuset_destroy.add_argument('name', nargs='?', type=str, metavar='NAME', help='Cpuset name to destroy (use this OR --pattern)') + cpuset_destroy.add_argument('names', nargs='*', type=str, metavar='NAME', help='Cpuset name(s) to destroy (one or more, OR use --pattern)') cpuset_destroy.add_argument('-p', '--pattern', type=str, metavar='PATTERN', help='Glob pattern to destroy multiple cpusets (e.g., tuna*)') cpuset_destroy.add_argument('-f', '--force', action='store_true', help='Migrate tasks to root cgroup before destroying') cpuset_destroy.add_argument('--include-empty', action='store_true', help='When using --pattern, include cpusets with no CPUs assigned (default: skip empty)') @@ -1140,12 +1140,12 @@ def cpuset_list(pattern, verbose, show_empty, show_system, recursive): print("This may indicate a problem with blocklist protection.", file=sys.stderr) -def cpuset_destroy(name, pattern, force, include_empty, recursive): +def cpuset_destroy(names, pattern, force, include_empty, recursive): """Handler for 'tuna cpuset destroy' command. Args: - name: Cpuset name to destroy (single cpuset, mutually exclusive with pattern) - pattern: Glob pattern for cpusets to destroy (mutually exclusive with name) + names: List of cpuset names to destroy (mutually exclusive with pattern) + pattern: Glob pattern for cpusets to destroy (mutually exclusive with names) force: If True, migrate tasks to root before destroying include_empty: If True, include cpusets with no CPUs assigned (default: skip empty, only used with pattern) recursive: If True, find and clean nested cpusets (only used with pattern) @@ -1156,19 +1156,20 @@ def cpuset_destroy(name, pattern, force, include_empty, recursive): print("Error: cgroup v2 cpusets not supported on this system", file=sys.stderr) sys.exit(1) - # Validate: must have either name OR pattern, not both, not neither - if name and pattern: - print("Error: specify either NAME or --pattern, not both", file=sys.stderr) + # Validate: must have either names OR pattern, not both, not neither + if names and pattern: + print("Error: specify either NAME(s) or --pattern, not both", file=sys.stderr) sys.exit(2) - if not name and not pattern: - print("Error: must specify either NAME or --pattern", file=sys.stderr) + if not names and not pattern: + print("Error: must specify either NAME(s) or --pattern", file=sys.stderr) sys.exit(2) try: - if name: - # Single cpuset destroy (include_empty not applicable) - cpuset.destroy_cpuset(name, force=force) - print(f"Destroyed cpuset '{name}'") + if names: + # Destroy specified cpusets by name (one or more) + for name in names: + cpuset.destroy_cpuset(name, force=force) + print(f"Destroyed cpuset '{name}'") else: # Pattern-based cleanup if not include_empty: @@ -1879,7 +1880,7 @@ def main(): cpuset_list(args.pattern, args.verbose, args.show_empty, args.show_system, args.recursive) elif args.cpuset_command == 'destroy': - cpuset_destroy(args.name, args.pattern, args.force, args.include_empty, args.recursive) + cpuset_destroy(args.names, args.pattern, args.force, args.include_empty, args.recursive) elif args.cpuset_command == 'move': cpuset_move(args.name, args.thread_list, args.pid_list) -- 2.55.0