diff --git a/SOURCES/tuna-Adapt-show_threads-cgroup-output-to-terminal-si.patch b/SOURCES/tuna-Adapt-show_threads-cgroup-output-to-terminal-si.patch
new file mode 100644
index 0000000..530c1c4
--- /dev/null
+++ b/SOURCES/tuna-Adapt-show_threads-cgroup-output-to-terminal-si.patch
@@ -0,0 +1,148 @@
+From b04ce6fa8b183f6a930fbff240eff44efe801f91 Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Mon, 14 Nov 2022 14:55:08 -0500
+Subject: [PATCH] tuna: Adapt show_threads cgroup output to terminal size
+
+Passing the --cgroup flag to the show_threads command currently displays
+long cgroup strings on the thread output and decreases readability.
+
+Adapt the show_threads output to account for output string and terminal
+size, and format output accordingly to improve readability. Add
+--spaced flag to show_threads to print cgroups with spacing in
+between thread outputs.
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+
+---
+target branch: main
+
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/docs/tuna.8 b/docs/tuna.8
+index f50a8c2a0a16..242389455f83 100644
+--- a/docs/tuna.8
++++ b/docs/tuna.8
+@@ -188,6 +188,7 @@ optional arguments:
+                         Operation will affect children threads
+   -G, --cgroups         Display the processes with the type of cgroups they
+                         are in
++  -z, --spaced          Display spaced view for cgroups
+ 
+ .TP
+ \fBtuna show_irqs\fR
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index 8be35f7fb4c4..630c8bc60deb 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -114,6 +114,7 @@ def gen_parser():
+             "sockets": dict(dest='cpu_list', default=[], metavar='CPU-SOCKET-LIST', type=socketstring_to_list, help="CPU-SOCKET-LIST affected by commands"),
+             "show_sockets": dict(action='store_true', help='Show network sockets in use by threads'),
+             "cgroups": dict(action='store_true', dest='cgroups', help='Display the processes with the type of cgroups they are in'),
++            "spaced": dict(action='store_false', dest='compact', help='Display spaced view for cgroups'),
+             "affect_children": dict(action='store_true', help="Operation will affect children threads"),
+             "nohz_full": dict(action='store_true', help="CPUs in nohz_full kernel command line will be affected by operations"),
+             "no_uthreads": dict(action='store_false', dest='uthreads', help="Operations will not affect user threads"),
+@@ -215,6 +216,7 @@ def gen_parser():
+     if have_inet_diag:
+         show_threads.add_argument('-n', '--show_sockets', **MODS['show_sockets'])
+     show_threads.add_argument('-G', '--cgroups', **MODS['cgroups'])
++    show_threads.add_argument('-z', '--spaced', **MODS['spaced'])
+ 
+ 
+     show_irqs_group = show_irqs.add_mutually_exclusive_group(required=False)
+@@ -335,7 +337,7 @@ def format_affinity(affinity):
+     return ",".join(str(hex(a)) for a in procfs.hexbitmask(affinity, get_nr_cpus()))
+ 
+ def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes,
+-                   sock_inode_re, cgroups):
++                   sock_inode_re, cgroups, columns=None, compact=True):
+     global irqs
+     try:
+         affinity = format_affinity(os.sched_getaffinity(pid))
+@@ -372,10 +374,20 @@ def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes,
+                                           nonvoluntary_ctxt_switches)
+ 
+     # Indent affected children
+-    print(" %-5d " % pid if affect_children else "  %-5d" % pid, end=' ')
+-    print("%6s %5d %8s%s %15s %s" % (sched, rtprio, affinity,
+-                                     ctxt_switch_info, cmd, users), end=' ')
+-    print(" %9s" % cgout if cgroups else "")
++    s1 = " %-5d " % pid if affect_children else "  %-5d" % pid
++    print(s1, end=' ')
++    s2 = "%6s %5d %8s%s %15s     %s" % (sched, rtprio, affinity,
++                                     ctxt_switch_info, cmd, users)
++    print(s2, end=' ')
++
++    if cgroups:
++        length = int(columns) - len(s1 + " ") - len(s2 + " ")
++        if len(" %9s" % cgout) <= length:
++            print("%s" % cgout)
++        else:
++            print("\n %s" % cgout + ("" if compact else "\n"))
++    else:
++        print()
+ 
+     if sock_inodes:
+         ps_show_sockets(pid, ps, sock_inodes, sock_inode_re,
+@@ -384,12 +396,12 @@ def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes,
+         for tid in list(ps[pid]["threads"].keys()):
+             ps_show_thread(tid, False, ps[pid]["threads"],
+                            has_ctxt_switch_info,
+-                           sock_inodes, sock_inode_re, cgroups)
++                           sock_inodes, sock_inode_re, cgroups, columns, compact)
+ 
+ 
+ def ps_show(ps, affect_children, thread_list, cpu_list,
+             irq_list_numbers, show_uthreads, show_kthreads,
+-            has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups):
++            has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact):
+ 
+     ps_list = []
+     for pid in list(ps.keys()):
+@@ -426,9 +438,15 @@ def ps_show(ps, affect_children, thread_list, cpu_list,
+ 
+     ps_list.sort()
+ 
++
++    # Width of terminal in columns
++    columns = None
++    if cgroups:
++        _, columns = os.popen('stty size', 'r').read().split()
++
+     for pid in ps_list:
+         ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info,
+-                       sock_inodes, sock_inode_re, cgroups)
++                       sock_inodes, sock_inode_re, cgroups, columns, compact)
+ 
+ 
+ def load_socktype(socktype, inodes):
+@@ -449,7 +467,7 @@ def load_sockets():
+ 
+ 
+ def do_ps(thread_list, cpu_list, irq_list, show_uthreads, show_kthreads,
+-          affect_children, show_sockets, cgroups):
++          affect_children, show_sockets, cgroups, compact):
+     ps = procfs.pidstats()
+     if affect_children:
+         ps.reload_threads()
+@@ -466,7 +484,7 @@ def do_ps(thread_list, cpu_list, irq_list, show_uthreads, show_kthreads,
+             ps_show_header(has_ctxt_switch_info, cgroups)
+         ps_show(ps, affect_children, thread_list,
+                 cpu_list, irq_list, show_uthreads, show_kthreads,
+-                has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups)
++                has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact)
+     except IOError:
+         # 'tuna -P | head' for instance
+         pass
+@@ -698,7 +716,7 @@ def main():
+ 
+     elif args.command in ['show_threads']:
+         do_ps(args.thread_list, args.cpu_list, args.irq_list, args.uthreads,
+-                args.kthreads, args.affect_children, args.show_sockets if "show_sockets" in args else None, args.cgroups)
++                args.kthreads, args.affect_children, args.show_sockets if "show_sockets" in args else None, args.cgroups, args.compact)
+ 
+     elif args.command in ['show_irqs']:
+         show_irqs(args.irq_list, args.cpu_list)
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-Add-sockets-command-line-option.patch b/SOURCES/tuna-Add-sockets-command-line-option.patch
new file mode 100644
index 0000000..d6f829b
--- /dev/null
+++ b/SOURCES/tuna-Add-sockets-command-line-option.patch
@@ -0,0 +1,137 @@
+From 87f6d9e29bab615b03b26210e3ead493fd08fe1f Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Thu, 8 Sep 2022 14:49:35 -0400
+Subject: [PATCH 3/6] tuna: Add --sockets command line option
+
+The getopt version of tuna implements the --sockets argument which is used
+to specify a cpulist from the cpu sockets on a system. The --sockets
+and --cpus arguments in the getopt version override each other, such
+that 'tuna -S 0 -c 1,2' will cause a cpu_list of [1, 2].
+
+In the argparse cli, add support for a --sockets argument used to specify a
+cpulist and is mutually exclusive with the --cpus and --nohz_full arguments.
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index 554912057f03..9a3d3f32398b 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -112,7 +112,7 @@ def gen_parser():
+             "threads": dict(dest='thread_list', default=[], metavar='THREAD-LIST', type=threadstring_to_list, help="THREAD-LIST affected by commands"),
+             "irqs": dict(dest='irq_list', default=[], metavar='IRQ-LIST', type=irqstring_to_list, help="IRQ-LIST affect by commands"),
+             "cpus": dict(dest='cpu_list', default=[], metavar='CPU-LIST', type=tuna.cpustring_to_list, help='CPU-LIST affected by commands'),
+-            "sockets": dict(default=[], nargs='+', type=int, help="CPU-SOCKET-LIST affected by commands"),
++            "sockets": dict(dest='cpu_list', default=[], metavar='CPU-SOCKET-LIST', type=socketstring_to_list, help="CPU-SOCKET-LIST affected by commands"),
+             "show_sockets": dict(action='store_true', help='Show network sockets in use by threads'),
+             "cgroups": dict(action='store_true', dest='cgroups', help='Display the processes with the type of cgroups they are in'),
+             "affect_children": dict(action='store_true', help="Operation will affect children threads"),
+@@ -159,20 +159,24 @@ def gen_parser():
+ 
+     isolate_group = isolate.add_mutually_exclusive_group(required=True)
+     isolate_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    isolate_group.add_argument('-S', '--sockets', **MODS['sockets'])
+     isolate_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
+ 
+     include_group = include.add_mutually_exclusive_group(required=True)
+     include_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    include_group.add_argument('-S', '--sockets', **MODS['sockets'])
+     include_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
+ 
+     move_group = move.add_mutually_exclusive_group(required=True)
+     move_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    move_group.add_argument('-S', '--sockets', **MODS['sockets'])
+     move_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
+     move.add_argument('-t', '--threads', **MODS['threads'])
+     move.add_argument('-q', '--irqs', **MODS['irqs'])
+ 
+     spread_group = spread.add_mutually_exclusive_group(required=True)
+     spread_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    spread_group.add_argument('-S', '--sockets', **MODS['sockets'])
+     spread_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
+     spread.add_argument('-t', '--threads', **MODS['threads'])
+     spread.add_argument('-q', '--irqs', **MODS['irqs'])
+@@ -182,12 +186,18 @@ def gen_parser():
+     priority.add_argument('-C', '--affect_children', **MODS['affect_children'])
+ 
+     run.add_argument('run_command', **POS['run_command'])
+-    run.add_argument('-c', '--cpus', **MODS['cpus'])
++    run_group = run.add_mutually_exclusive_group(required=False)
++    run_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    run_group.add_argument('-S', '--sockets', **MODS['sockets'])
++    run_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
+     run.add_argument('-p', '--priority', **MODS['priority'])
+     run.add_argument('-b', '--background', **MODS['background'])
+ 
+     save.add_argument('filename', **POS['filename'])
+-    save.add_argument('-c', '--cpus', **MODS['cpus'])
++    save_group = save.add_mutually_exclusive_group(required=False)
++    save_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    save_group.add_argument('-S', '--sockets', **MODS['sockets'])
++    save_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
+     save.add_argument('-t', '--threads', **MODS['threads'])
+ 
+     apply.add_argument('profilename', **POS['profilename'])
+@@ -195,6 +205,7 @@ def gen_parser():
+     show_threads_group1 = show_threads.add_mutually_exclusive_group(required=False)
+     show_threads_group1.add_argument('-c', '--cpus', **MODS['cpus'])
+     show_threads_group1.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
++    show_threads_group1.add_argument('-S', '--sockets', **MODS['sockets'])
+     show_threads_group2 = show_threads.add_mutually_exclusive_group(required=False)
+     show_threads_group2.add_argument('-t', '--threads', **MODS['threads'])
+     show_threads_group2.add_argument('-q', '--irqs', **MODS['irqs'])
+@@ -206,14 +217,21 @@ def gen_parser():
+         show_threads.add_argument('-n', '--show_sockets', **MODS['show_sockets'])
+     show_threads.add_argument('-G', '--cgroups', **MODS['cgroups'])
+ 
+-    show_irqs.add_argument('-c', '--cpus', **MODS['cpus'])
++
++    show_irqs_group = show_irqs.add_mutually_exclusive_group(required=False)
++    show_irqs_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    show_irqs_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
++    show_irqs_group.add_argument('-S', '--sockets', **MODS['sockets'])
+     show_irqs.add_argument('-q', '--irqs', **MODS['irqs'])
+ 
+     what_is.add_argument('thread_list', **POS['thread_list'])
+ 
+     gui.add_argument('-d', '--disable_perf', **MODS['disable_perf'])
+     gui.add_argument('-R', '--refresh', **MODS['refresh'])
+-    gui.add_argument('-c', '--cpus', **MODS['cpus'])
++    gui_group = gui.add_mutually_exclusive_group(required=False)
++    gui_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    gui_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
++    gui_group.add_argument('-S', '--sockets', **MODS['sockets'])
+     gui.add_argument('-U', '--no_uthreads', **MODS['no_uthreads'])
+     gui.add_argument('-K', '--no_kthreads', **MODS['no_kthreads'])
+ 
+@@ -536,7 +554,6 @@ def irqstring_to_list(irqstr):
+ 
+     irq_list = []
+     irq_strings = list(set(irqstr.split(',')))
+-    print(irq_strings)
+     for s in irq_strings:
+         if s.isdigit():
+             irq_list.append(int(s))
+@@ -546,6 +563,18 @@ def irqstring_to_list(irqstr):
+             irq_list += [int(i) for i in irq_list_str if i.isdigit()]
+     return irq_list
+ 
++def socketstring_to_list(socketstr):
++    cpu_list = []
++    socket_strings = list(set(socketstr.split(',')))
++    cpu_info = sysfs.cpus()
++
++    for s in socket_strings:
++        if s not in cpu_info.sockets:
++            print("tuna: invalid socket %(socket)s sockets available: %(available)s" %
++                    {"socket": s,"available": ",".join(list(cpu_info.sockets.keys()))})
++            sys.exit(2)
++        cpu_list += [int(cpu.name[3:]) for cpu in cpu_info.sockets[s]]
++    return cpu_list
+ 
+ def pick_op(argument):
+     if argument == "":
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-Fix-matching-irqs-in-ps_show_thread.patch b/SOURCES/tuna-Fix-matching-irqs-in-ps_show_thread.patch
new file mode 100644
index 0000000..b218c88
--- /dev/null
+++ b/SOURCES/tuna-Fix-matching-irqs-in-ps_show_thread.patch
@@ -0,0 +1,49 @@
+From 3f1fbb092f5ef07d04fef7ddec9e538f36d84450 Mon Sep 17 00:00:00 2001
+From: John Kacur <jkacur@redhat.com>
+Date: Fri, 2 Sep 2022 11:55:07 -0400
+Subject: [PATCH 5/6] tuna: Fix matching irqs in ps_show_thread
+
+To new format to match irqs is "irqs/"
+We already test this when we call is_irq_thread(cmd):
+
+With this fix if you do
+
+tuna show_threads
+
+You will get nic information that was previously missed such as
+
+  986     FIFO    50        9     69286            2 irq/164-iwlwifi:default_que
+ue iwlwifi:default_queue
+
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index b13b25b8a801..80e27523acc6 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -351,17 +351,12 @@ def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes,
+         try:
+             if not irqs:
+                 irqs = procfs.interrupts()
+-            if cmd[:4] == "IRQ-":
+-                users = irqs[tuna.irq_thread_number(cmd)]["users"]
+-                for u in users:
+-                    if u in get_nics():
+-                        users[users.index(u)] = "%s(%s)" % (
+-                            u, ethtool.get_module(u))
+-                users = ",".join(users)
+-            else:
+-                u = cmd[cmd.find('-') + 1:]
++            users = irqs[tuna.irq_thread_number(cmd)]["users"]
++            for u in users:
+                 if u in get_nics():
+-                    users = ethtool.get_module(u)
++                    users[users.index(u)] = "%s(%s)" % (
++                        u, ethtool.get_module(u))
++            users = ",".join(users)
+         except:
+             users = "Not found in /proc/interrupts!"
+ 
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-Fix-show_threads-cgroups-run-without-a-term.patch b/SOURCES/tuna-Fix-show_threads-cgroups-run-without-a-term.patch
new file mode 100644
index 0000000..e8afb68
--- /dev/null
+++ b/SOURCES/tuna-Fix-show_threads-cgroups-run-without-a-term.patch
@@ -0,0 +1,42 @@
+From 4ccef8c2996e59e7031e77d7f8e2b42b73036210 Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Wed, 23 Nov 2022 14:14:36 -0500
+Subject: [PATCH] tuna: Fix show_threads --cgroups run without a term
+
+When tuna show_threads --cgroups is run without a term,
+provide a default column size of 80 to fix a traceback
+that occurred when querying the terminal size.
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+- Edited the commit title
+- Edited the description
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index 630c8bc..4e809dd 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -31,6 +31,7 @@ import procfs
+ from tuna import tuna, sysfs
+ import logging
+ import time
++import shutil
+ 
+ def get_loglevel(level):
+     if level.isdigit() and int(level) in range(0,5):
+@@ -440,9 +441,10 @@ def ps_show(ps, affect_children, thread_list, cpu_list,
+ 
+ 
+     # Width of terminal in columns
+-    columns = None
++    columns = 80
+     if cgroups:
+-        _, columns = os.popen('stty size', 'r').read().split()
++        if os.isatty(sys.stdout.fileno()):
++            columns = shutil.get_terminal_size().columns
+ 
+     for pid in ps_list:
+         ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info,
+-- 
+2.38.1
+
diff --git a/SOURCES/tuna-Move-get_policy_and_rtprio-call-to-parser-level.patch b/SOURCES/tuna-Move-get_policy_and_rtprio-call-to-parser-level.patch
new file mode 100644
index 0000000..45c4fb6
--- /dev/null
+++ b/SOURCES/tuna-Move-get_policy_and_rtprio-call-to-parser-level.patch
@@ -0,0 +1,48 @@
+From 9dd8c3e889fe632405561cc8476ed42659a99e2e Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Thu, 4 Aug 2022 09:39:11 -0400
+Subject: [PATCH 2/6] tuna: Move get_policy_and_rtprio call to parser level
+
+Due to the implementation of the parser for the priority action,
+the valid policy and rtprio input can be checked at the parser level.
+
+Call get_policy_and_rtprio on `priority` and `run --priority` commands
+at argparse level.
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index f49cce4d84d4..554912057f03 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -102,7 +102,7 @@ def gen_parser():
+             "filename": dict(metavar='FILENAME', type=str, help="Save kthreads sched tunables to this file"),
+             "profilename": dict(type=str, help="Apply changes described in this file"),
+             "run_command": dict(metavar='COMMAND', type=str, help="fork a new process and run the \"COMMAND\""),
+-            "priority": dict(type=str, help="policy/priority help"),
++            "priority": dict(type=tuna.get_policy_and_rtprio, help="policy/priority help"),
+           }
+ 
+     MODS = {
+diff --git a/tuna/tuna.py b/tuna/tuna.py
+index 8650f11c81c3..30a5a570c9d4 100755
+--- a/tuna/tuna.py
++++ b/tuna/tuna.py
+@@ -558,11 +558,8 @@ def thread_set_priority(tid, policy, rtprio):
+     os.sched_setscheduler(tid, policy, param)
+ 
+ def threads_set_priority(tids, parm, affect_children=False):
+-    try:
+-        (policy, rtprio) = get_policy_and_rtprio(parm)
+-    except ValueError:
+-        print("tuna: " + _("\"%s\" is an unsupported priority value!") % parm[0])
+-        return
++
++    (policy, rtprio) = parm
+ 
+     for tid in tids:
+         try:
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-Remove-threads-print-statement.patch b/SOURCES/tuna-Remove-threads-print-statement.patch
new file mode 100644
index 0000000..297038e
--- /dev/null
+++ b/SOURCES/tuna-Remove-threads-print-statement.patch
@@ -0,0 +1,24 @@
+From b82d9037fe7f51e49a9b3c84b1ccb71fda1e1f61 Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Tue, 25 Oct 2022 16:19:25 -0400
+Subject: [PATCH 6/6] tuna: Remove threads print statement
+
+Remove unnecessary print statement from threadstring_to_list function
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index 80e27523acc6..c5bc65059da7 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -533,7 +533,6 @@ def threadstring_to_list(threadstr):
+     global ps
+     thread_list = []
+     thread_strings = list(set(threadstr.split(',')))
+-    print(thread_strings)
+     for s in thread_strings:
+         if s.isdigit():
+             thread_list.append(int(s))
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-Replace-python_ethtool-with-builtin-funtionalit.patch b/SOURCES/tuna-Replace-python_ethtool-with-builtin-funtionalit.patch
new file mode 100644
index 0000000..b8408f6
--- /dev/null
+++ b/SOURCES/tuna-Replace-python_ethtool-with-builtin-funtionalit.patch
@@ -0,0 +1,97 @@
+From f2a28b05264fa9557192b73a1b888756748930ac Mon Sep 17 00:00:00 2001
+From: John Kacur <jkacur@redhat.com>
+Date: Tue, 27 Sep 2022 12:59:54 -0400
+Subject: [PATCH 4/6] tuna: Replace python_ethtool with builtin funtionality
+
+This patch replaces the dependency on python_ethtool with some
+simplified functions to achieve the same result.
+
+Reviewed-by: Federico Pellegrin <fede@evolware.org>
+- return 'tun' only if tun_flags exists
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index 9a3d3f32398b..b13b25b8a801 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -26,7 +26,7 @@ import fnmatch
+ import gettext
+ import locale
+ from functools import reduce
+-import ethtool
++import tuna.new_eth as ethtool
+ import tuna.tuna_sched as tuna_sched
+ import procfs
+ from tuna import tuna, sysfs
+diff --git a/tuna/gui/irqview.py b/tuna/gui/irqview.py
+index 35fc3fd0b0ca..5143d6dc0df5 100755
+--- a/tuna/gui/irqview.py
++++ b/tuna/gui/irqview.py
+@@ -7,7 +7,7 @@ from gi.repository import Gtk
+ from gi.repository import GObject
+ import os
+ from functools import reduce
+-import ethtool
++import tuna.new_eth as ethtool
+ import tuna.tuna_sched as tuna_sched
+ 
+ import gi
+diff --git a/tuna/new_eth.py b/tuna/new_eth.py
+new file mode 100755
+index 000000000000..98f9179d5695
+--- /dev/null
++++ b/tuna/new_eth.py
+@@ -0,0 +1,37 @@
++# Copyright (C) 2022 John Kacur
++""" A few functions similar to ethtool """
++import os
++import socket
++
++def get_active_devices():
++    """ return a list of network devices """
++    ret = []
++
++    for device in socket.if_nameindex():
++        ret.append(device[1])
++
++    return ret
++
++def get_module(intf):
++    """ return the kernel module for the given network interface """
++    if intf == 'lo':
++        return ""
++    myp = f'/sys/class/net/{intf}/device/driver'
++    if os.path.exists(myp):
++        return os.path.basename(os.readlink(myp))
++    if os.path.exists(f'/sys/class/net/{intf}/bridge'):
++        return 'bridge'
++    if os.path.exists(f'/sys/class/net/{intf}/tun_flags'):
++        return 'tun'
++    return ""
++
++if __name__ == "__main__":
++    nics = get_active_devices()
++    print(f'nics = {nics}')
++
++    for intf in nics:
++        driver = get_module(intf)
++        if driver:
++            print(f'{intf}, {driver}')
++        else:
++            print(f'{intf}')
+diff --git a/tuna/tuna.py b/tuna/tuna.py
+index 30a5a570c9d4..43adb84079e4 100755
+--- a/tuna/tuna.py
++++ b/tuna/tuna.py
+@@ -9,7 +9,7 @@ import sys
+ import shlex
+ import fnmatch
+ import platform
+-import ethtool
++import tuna.new_eth as ethtool
+ import procfs
+ from procfs import utilist
+ from tuna import help
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-Update-command-line-interface.patch b/SOURCES/tuna-Update-command-line-interface.patch
new file mode 100644
index 0000000..d86331e
--- /dev/null
+++ b/SOURCES/tuna-Update-command-line-interface.patch
@@ -0,0 +1,733 @@
+From eba1333c50f8a5eebb12b523c30faffac33681b3 Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Thu, 4 Aug 2022 09:39:10 -0400
+Subject: [PATCH 1/6] tuna: Update command-line interface
+
+The current command-line interface allows for chaining of multiple
+actions, which results in ambiguous user input, a need for maintaining
+variable states (thread-list, cpu-list, etc.) in the program, and a need
+to type check input after parsing. The help menu can also be
+overwhelming.
+
+Allow for the use of argparse rather than getopt to significantly
+simplify the program.
+Provide subcommands and clearer usage menus for the user,
+introduce type checking at the parser level, and remove
+ambiguity for user input.
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index bdaa70ffc156..f49cce4d84d4 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -14,8 +14,9 @@
+ #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ #   General Public License for more details.
+ 
+-""" tuna - Application Tuning GUI """
++""" tuna - Application Tuning Program"""
+ 
++import argparse
+ import os
+ import sys
+ import errno
+@@ -37,7 +38,7 @@ def get_loglevel(level):
+         # logging built-in module levels:
+         # 0 - NOTSET
+         # 10 - DEBUG
+-        # 20 - INFO,
++        # 20 - INFO
+         # 30 - WARNING
+         # 40 - ERROR
+         return int(level) * 10
+@@ -85,70 +86,138 @@ except:
+ nr_cpus = None
+ ps = None
+ irqs = None
+-version = "0.18"
+-
+-
+-def usage():
+-    print(_('Usage: tuna [OPTIONS]'))
+-    fmt = '\t%-40s %s'
+-    print(fmt % ('-h, --help',                  _('Give this help list')))
+-    print(fmt % ('-a, --config_file_apply=profilename',
+-                 _('Apply changes described in profile')))
+-    print(fmt % ('-l, --config_file_list',
+-                 _('List preloaded profiles')))
+-    print(fmt % ('-g, --gui',                   _('Start the GUI')))
+-    print(fmt % ('-G, --cgroup',
+-                 _('Display the processes with the type of cgroups they are in')))
+-    print(fmt % ('-c, --cpus=' + _('CPU-LIST'), _('%(cpulist)s affected by commands') %
+-                 {"cpulist": _('CPU-LIST')}))
+-    print(fmt % ('-C, --affect_children',
+-                 _('Operation will affect children threads')))
+-    print(fmt % ('-d, --disable_perf',
+-                 _('Explicitly disable usage of perf in GUI for process view')))
+-    print(fmt % ('-D, --debug', _('Print DEBUG level logging details to console')))
+-    print(fmt % ('-f, --filter',
+-                 _('Display filter the selected entities')))
+-    print(fmt % ('-i, --isolate',               _('Move all allowed threads and IRQs away from %(cpulist)s') %
+-                 {"cpulist": _('CPU-LIST')}))
+-    print(fmt % ('-I, --include',               _('Allow all allowed threads and IRQs to run on %(cpulist)s') %
+-                 {"cpulist": _('CPU-LIST')}))
+-    print(fmt % ('-K, --no_kthreads',
+-                 _('Operations will not affect kernel threads')))
+-    print(fmt % ('-L, --logging',
+-                 _('Log application details to log file for given LOG-LEVEL')))
+-    print(fmt % ('-m, --move',                  _('Move selected entities to %(cpulist)s') %
+-                 {"cpulist": _('CPU-LIST')}))
+-    print(fmt % ('-N, --nohz_full',
+-                 _('CPUs in nohz_full= kernel command line will be affected by operations')))
++
++class HelpMessageParser(argparse.ArgumentParser):
++    def error(self, message):
++        sys.stderr.write('error: %s\n' % message)
++        self.print_help()
++        sys.exit(2)
++
++def gen_parser():
++
++
++    POS = {
++            "cpu_list": dict(metavar='CPU-LIST', type=tuna.cpustring_to_list, help="CPU-LIST affected by commands"),
++            "thread_list": dict(metavar='THREAD-LIST', type=threadstring_to_list, help="THREAD-LIST affected by commands"),
++            "filename": dict(metavar='FILENAME', type=str, help="Save kthreads sched tunables to this file"),
++            "profilename": dict(type=str, help="Apply changes described in this file"),
++            "run_command": dict(metavar='COMMAND', type=str, help="fork a new process and run the \"COMMAND\""),
++            "priority": dict(type=str, help="policy/priority help"),
++          }
++
++    MODS = {
++            "logging": dict(dest='loglevel', metavar='LOG-LEVEL', type=get_loglevel, help="Log application details to file for given LOG-LEVEL"),
++            "debug" : dict(action='store_true', dest='debug', help='Print DEBUG level logging details to console'),
++            "version": dict(action='version', version='0.18', help="show version"),
++            "threads": dict(dest='thread_list', default=[], metavar='THREAD-LIST', type=threadstring_to_list, help="THREAD-LIST affected by commands"),
++            "irqs": dict(dest='irq_list', default=[], metavar='IRQ-LIST', type=irqstring_to_list, help="IRQ-LIST affect by commands"),
++            "cpus": dict(dest='cpu_list', default=[], metavar='CPU-LIST', type=tuna.cpustring_to_list, help='CPU-LIST affected by commands'),
++            "sockets": dict(default=[], nargs='+', type=int, help="CPU-SOCKET-LIST affected by commands"),
++            "show_sockets": dict(action='store_true', help='Show network sockets in use by threads'),
++            "cgroups": dict(action='store_true', dest='cgroups', help='Display the processes with the type of cgroups they are in'),
++            "affect_children": dict(action='store_true', help="Operation will affect children threads"),
++            "nohz_full": dict(action='store_true', help="CPUs in nohz_full kernel command line will be affected by operations"),
++            "no_uthreads": dict(action='store_false', dest='uthreads', help="Operations will not affect user threads"),
++            "no_kthreads": dict(action='store_false', dest='kthreads', help="Operations will not affect kernel threads"),
++            "disable_perf": dict(action='store_true', help="Explicitly disable usage of perf in GUI for process view"),
++            "refresh": dict(default=2500, metavar='MSEC', type=int, help="Refresh the GUI every MSEC milliseconds"),
++            "priority": dict(default=(None, None), type=tuna.get_policy_and_rtprio, help="Set thread scheduler tunables: POLICY and RTPRIO"),
++            "background": dict(action='store_true', help="Run command as background task")
++         }
++
++    parser = HelpMessageParser(description="tuna - Application Tuning Program")
++
++    parser._positionals.title = "commands"
++    parser.add_argument('-v', '--version', **MODS['version'])
++    parser.add_argument('-L', '--logging', **MODS['logging'])
++    parser.add_argument('-D', '--debug', **MODS['debug'])
++
++    subparser = parser.add_subparsers(dest='command')
++
++    isolate = subparser.add_parser('isolate', description="Move all allowed threads and IRQs away from CPU-LIST",
++                                    help="Move all allowed threads and IRQs away from CPU-LIST")
++    include = subparser.add_parser('include', description="Allow all threads to run on CPU-LIST",
++                                     help="Allow all threads to run on CPU-LIST")
++    move = subparser.add_parser('move', description="Move selected entities to CPU-LIST",
++                                    help="Move selected entities to CPU-LIST")
++    spread = subparser.add_parser('spread', description="Move selected entities to CPU-LIST",
++                                    help="Spread selected entities over CPU-LIST")
++    priority = subparser.add_parser('priority', description="Set thread scheduler tunables: POLICY and RTPRIO",
++                                    help="Set thread scheduler tunables: POLICY and RTPRIO")
++    run = subparser.add_parser('run', description="Fork a new process and run the COMMAND",
++                                help="Fork a new process and run the COMMAND")
++    save = subparser.add_parser('save', description="Save kthreads sched tunables to FILENAME",
++                                help="Save kthreads sched tunables to FILENAME")
++    apply = subparser.add_parser('apply', description="Apply changes described in profile",
++                                help="Apply changes described in profile")
++    show_threads = subparser.add_parser('show_threads', description='Show thread list', help='Show thread list')
++    show_irqs = subparser.add_parser('show_irqs', description='Show IRQ list', help='Show IRQ list')
++    show_configs = subparser.add_parser('show_configs', description='List preloaded profiles', help='List preloaded profiles')
++
++    what_is = subparser.add_parser('what_is', description='Provides help about selected entities', help='Provides help about selected entities')
++    gui = subparser.add_parser('gui', description="Start the GUI", help="Start the GUI")
++
++    isolate_group = isolate.add_mutually_exclusive_group(required=True)
++    isolate_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    isolate_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
++
++    include_group = include.add_mutually_exclusive_group(required=True)
++    include_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    include_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
++
++    move_group = move.add_mutually_exclusive_group(required=True)
++    move_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    move_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
++    move.add_argument('-t', '--threads', **MODS['threads'])
++    move.add_argument('-q', '--irqs', **MODS['irqs'])
++
++    spread_group = spread.add_mutually_exclusive_group(required=True)
++    spread_group.add_argument('-c', '--cpus', **MODS['cpus'])
++    spread_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
++    spread.add_argument('-t', '--threads', **MODS['threads'])
++    spread.add_argument('-q', '--irqs', **MODS['irqs'])
++
++    priority.add_argument('priority', **POS['priority'])
++    priority.add_argument('-t', '--threads', **MODS['threads'], required=True)
++    priority.add_argument('-C', '--affect_children', **MODS['affect_children'])
++
++    run.add_argument('run_command', **POS['run_command'])
++    run.add_argument('-c', '--cpus', **MODS['cpus'])
++    run.add_argument('-p', '--priority', **MODS['priority'])
++    run.add_argument('-b', '--background', **MODS['background'])
++
++    save.add_argument('filename', **POS['filename'])
++    save.add_argument('-c', '--cpus', **MODS['cpus'])
++    save.add_argument('-t', '--threads', **MODS['threads'])
++
++    apply.add_argument('profilename', **POS['profilename'])
++
++    show_threads_group1 = show_threads.add_mutually_exclusive_group(required=False)
++    show_threads_group1.add_argument('-c', '--cpus', **MODS['cpus'])
++    show_threads_group1.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
++    show_threads_group2 = show_threads.add_mutually_exclusive_group(required=False)
++    show_threads_group2.add_argument('-t', '--threads', **MODS['threads'])
++    show_threads_group2.add_argument('-q', '--irqs', **MODS['irqs'])
++    show_threads.add_argument('-U', '--no_uthreads', **MODS['no_uthreads'])
++    show_threads.add_argument('-K', '--no_kthreads', **MODS['no_kthreads'])
++    show_threads.add_argument('-C', '--affect_children', **MODS['affect_children'])
++
+     if have_inet_diag:
+-        print(fmt % ('-n, --show_sockets',
+-                     _('Show network sockets in use by threads')))
+-    print(fmt % ('-p, --priority=[' +
+-                 _('POLICY') + ':]' +
+-                 _('RTPRIO'),                   _('Set thread scheduler tunables: %(policy)s and %(rtprio)s') %
+-                 {"policy": _('POLICY'), "rtprio": _('RTPRIO')}))
+-    print(fmt % ('-P, --show_threads',          _('Show thread list')))
+-    print(fmt % ('-Q, --show_irqs',             _('Show IRQ list')))
+-    print(fmt % ('-q, --irqs=' + _('IRQ-LIST'), _('%(irqlist)s affected by commands') %
+-                 {"irqlist": _('IRQ-LIST')}))
+-    print(fmt % ('-r, --run=' + _('COMMAND'),   _('fork a new process and run the %(command)s') %
+-                 {"command": _('COMMAND')}))
+-    print(fmt % ('-R, --refresh=' + _('MSEC'),   _('Refresh the GUI every MSEC milliseconds')))
+-    print(fmt % ('-s, --save=' + _('FILENAME'), _('Save kthreads sched tunables to %(filename)s') %
+-                 {"filename": _('FILENAME')}))
+-    print(fmt % ('-S, --sockets=' +
+-                 _('CPU-SOCKET-LIST'),          _('%(cpusocketlist)s affected by commands') %
+-                 {"cpusocketlist": _('CPU-SOCKET-LIST')}))
+-    print(fmt % ('-t, --threads=' +
+-                 _('THREAD-LIST'),              _('%(threadlist)s affected by commands') %
+-                 {"threadlist": _('THREAD-LIST')}))
+-    print(fmt % ('-U, --no_uthreads',
+-                 _('Operations will not affect user threads')))
+-    print(fmt % ('-v, --version',               _('Show version')))
+-    print(fmt % ('-W, --what_is',
+-                 _('Provides help about selected entities')))
+-    print(fmt % ('-x, --spread',                _('Spread selected entities over %(cpulist)s') %
+-                 {"cpulist": _('CPU-LIST')}))
++        show_threads.add_argument('-n', '--show_sockets', **MODS['show_sockets'])
++    show_threads.add_argument('-G', '--cgroups', **MODS['cgroups'])
++
++    show_irqs.add_argument('-c', '--cpus', **MODS['cpus'])
++    show_irqs.add_argument('-q', '--irqs', **MODS['irqs'])
++
++    what_is.add_argument('thread_list', **POS['thread_list'])
++
++    gui.add_argument('-d', '--disable_perf', **MODS['disable_perf'])
++    gui.add_argument('-R', '--refresh', **MODS['refresh'])
++    gui.add_argument('-c', '--cpus', **MODS['cpus'])
++    gui.add_argument('-U', '--no_uthreads', **MODS['no_uthreads'])
++    gui.add_argument('-K', '--no_kthreads', **MODS['no_kthreads'])
++
++    return parser
+ 
+ 
+ def get_nr_cpus():
+@@ -447,39 +516,34 @@ def do_list_op(op, current_list, op_list):
+         return list(set(current_list) - set(op_list))
+     return list(set(op_list))
+ 
+-
+-def thread_mapper(s):
++def threadstring_to_list(threadstr):
+     global ps
+-    try:
+-        return [int(s), ]
+-    except:
+-        pass
+-
+-    ps = procfs.pidstats()
+-
+-    try:
+-        return ps.find_by_regex(re.compile(fnmatch.translate(s)))
+-    except:
+-        return ps.find_by_name(s)
+-
++    thread_list = []
++    thread_strings = list(set(threadstr.split(',')))
++    print(thread_strings)
++    for s in thread_strings:
++        if s.isdigit():
++            thread_list.append(int(s))
++        else:
++            ps = procfs.pidstats()
++            try:
++                thread_list += ps.find_by_regex(re.compile(fnmatch.translate(s)))
++            except:
++                thread_list += ps.find_by_name(s)
++    return thread_list
+ 
+-def irq_mapper(s):
+-    global irqs
+-    try:
+-        return [int(s), ]
+-    except:
+-        pass
+-    if not irqs:
+-        irqs = procfs.interrupts()
++def irqstring_to_list(irqstr):
+ 
+-    irq_list_str = irqs.find_by_user_regex(re.compile(fnmatch.translate(s)))
+     irq_list = []
+-    for i in irq_list_str:
+-        try:
+-            irq_list.append(int(i))
+-        except:
+-            pass
+-
++    irq_strings = list(set(irqstr.split(',')))
++    print(irq_strings)
++    for s in irq_strings:
++        if s.isdigit():
++            irq_list.append(int(s))
++        else:
++            # find_by_user_regex returns a list of strings corresponding to irq number
++            irq_list_str = procfs.interrupts().find_by_user_regex(re.compile(fnmatch.translate(s)))
++            irq_list += [int(i) for i in irq_list_str if i.isdigit()]
+     return irq_list
+ 
+ 
+@@ -531,298 +595,123 @@ def list_config():
+         print(value)
+     sys.exit(1)
+ 
++def nohz_full_to_cpu():
++
++    try:
++        return tuna.nohz_full_list()
++    except:
++        print("tuna: --nohz_full " +
++              _(" needs nohz_full=cpulist on the kernel command line"))
++        sys.exit(2)
+ 
+ def main():
+     global ps
+ 
+     i18n_init()
+-    try:
+-        short = "a:c:dDCfgGhiIKlmNp:PQq:r:R:s:S:t:UvWxL:"
+-        long = ["cpus=", "affect_children", "filter", "gui", "help",
+-                "isolate", "include", "no_kthreads", "move", "nohz_full",
+-                "show_sockets", "priority=", "show_threads",
+-                "show_irqs", "irqs=",
+-                "save=", "sockets=", "threads=", "no_uthreads",
+-                "version", "what_is", "spread", "cgroup", "config_file_apply=",
+-                "config_file_list", "run=", "refresh=", "disable_perf", "logging=", "debug"]
+-        if have_inet_diag:
+-            short += "n"
+-            long.append("show_sockets")
+-        opts, args = getopt.getopt(sys.argv[1:], short, long)
+-    except getopt.GetoptError as err:
+-        usage()
+-        print(str(err))
+-        sys.exit(2)
++    parser = gen_parser()
++    # Set all necessary defaults for gui subparser if no arguments provided
++    args = parser.parse_args() if len(sys.argv) > 1 else parser.parse_args(['gui'])
++
++    if args.debug:
++        my_logger = setup_logging("my_logger")
++        my_logger.addHandler(add_handler("DEBUG", tofile=False))
++        my_logger.info("Debug option set")
++
++    if args.loglevel:
++        if not args.debug:
++            my_logger = setup_logging("my_logger")
++        try:
++            my_logger.addHandler(add_handler(args.loglevel, tofile=True))
++            my_logger.info("Logging option set at log level {}".format(args.loglevel))
+ 
+-    run_gui = not opts
+-    kthreads = True
+-    uthreads = True
+-    cgroups = False
+-    cpu_list = None
+-    debug = False
+-    irq_list = None
+-    irq_list_str = None
+-    log = False
+-    rtprio = None
+-    policy = None
+-    thread_list = []
+-    thread_list_str = None
+-    filter = False
+-    affect_children = False
+-    show_sockets = False
+-    p_waiting_action = False
+-    gui_refresh = 2500
+-    disable_perf = False
+-
+-    for o, a in opts:
+-        if o in ("-h", "--help"):
+-            usage()
+-            return
+-        if o in ("-D", "--debug"):
+-            if debug:
+-                my_logger.warning("Debugging already set")
+-                continue
+-            debug = True
+-            if not log:
+-                my_logger = setup_logging("my_logger")
+-            my_logger.addHandler(add_handler("DEBUG", tofile=False))
+-            my_logger.info("Debug option set")
+-        if o in ("-L", "--logging"):
+-            if log:
+-                my_logger.warning("Logging already set")
+-                continue
+-            log = True
+-            loglevel = get_loglevel(a)
+-            if not debug:
+-                my_logger = setup_logging("my_logger")
+-            try:
+-                my_logger.addHandler(add_handler(loglevel, tofile=True))
+-            except ValueError as e:
+-                print(e, "tuna: --logging requires valid logging level\n")
+-                print("Valid log levels: NOTSET, DEBUG, INFO, WARNING, ERROR")
+-                print("Log levels may be specified numerically (0-4)")
+-            my_logger.info("Logging option set")
+-        if o in ("-a", "--config_file_apply"):
+-            apply_config(a)
+-        elif o in ("-l", "--config_file_list"):
+-            list_config()
+-        elif o in ("-c", "--cpus"):
+-            (op, a) = pick_op(a)
+-            try:
+-                op_list = tuna.cpustring_to_list(a)
+-            except ValueError:
+-                usage()
+-                return
+-            cpu_list = do_list_op(op, cpu_list, op_list)
+-        elif o in ("-N", "--nohz_full"):
+-            try:
+-                cpu_list = tuna.nohz_full_list()
+-            except:
+-                print("tuna: --nohz_full " +
+-                      _(" needs nohz_full=cpulist on the kernel command line"))
+-                sys.exit(2)
+-        elif o in ("-C", "--affect_children"):
+-            affect_children = True
+-        elif o in ("-G", "--cgroup"):
+-            cgroups = True
+-        elif o in ("-t", "--threads"):
+-            # The -t - will reset thread list
+-            if a == '-':
+-                thread_list = []
+-                thread_list_str = ''
+-            else:
+-                (op, a) = pick_op(a)
+-                op_list = reduce(lambda i, j: i + j,
+-                                 list(map(thread_mapper, a.split(","))))
+-                op_list = list(set(op_list))
+-                thread_list = do_list_op(op, thread_list, op_list)
+-                # Check if a process name was specified and no
+-                # threads was found, which would result in an empty
+-                # thread list, i.e. we would print all the threads
+-                # in the system when we should print nothing.
+-                if not op_list and isinstance(a, type('')):
+-                    thread_list_str = do_list_op(op, thread_list_str,
+-                                                 a.split(","))
+-                if not op:
+-                    irq_list = None
+-        elif o in ("-f", "--filter"):
+-            filter = True
+-        elif o in ("-g", "--gui"):
+-            run_gui = True
+-        elif o in ("-R", "--refresh"):
+-            run_gui = True
+-            (op, a) = pick_op(a)
+-            try:
+-                gui_refresh=int(a)
+-            except Exception as err:
+-                print("tuna: --refresh %s" % err)
+-                sys.exit(2)
+-        elif o in ("-d", "--disable_perf"):
+-            run_gui = True
+-            disable_perf = True
+-        elif o in ("-i", "--isolate"):
+-            if not cpu_list:
+-                print("tuna: --isolate " + _("requires a cpu list!"))
+-                sys.exit(2)
+-            tuna.isolate_cpus(cpu_list, get_nr_cpus())
+-        elif o in ("-I", "--include"):
+-            if not cpu_list:
+-                print("tuna: --include " + _("requires a cpu list!"))
+-                sys.exit(2)
+-            tuna.include_cpus(cpu_list, get_nr_cpus())
+-        elif o in ("-p", "--priority"):
+-            # Save policy and rtprio for future Actions (e.g. --run).
+-            try:
+-                (policy, rtprio) = tuna.get_policy_and_rtprio(a)
+-            except ValueError:
+-                print("tuna: " + _("\"%s\" is an unsupported priority value!") % a)
+-                sys.exit(2)
+-            if not thread_list:
+-                # For backward compatibility
+-                p_waiting_action = True
+-            else:
+-                try:
+-                    tuna.threads_set_priority(thread_list, a, affect_children)
+-                except OSError as err:
+-                    print("tuna: %s" % err)
+-                    sys.exit(2)
+-        elif o in ("-P", "--show_threads"):
+-            # If the user specified process names that weren't
+-            # resolved to pids, don't show all threads.
+-            if not thread_list and not irq_list:
+-                if thread_list_str or irq_list_str:
+-                    continue
+-            do_ps(thread_list, cpu_list, irq_list, uthreads,
+-                  kthreads, affect_children, show_sockets, cgroups)
+-        elif o in ("-Q", "--show_irqs"):
+-            # If the user specified IRQ names that weren't
+-            # resolved to IRQs, don't show all IRQs.
+-            if not irq_list and irq_list_str:
+-                continue
+-            show_irqs(irq_list, cpu_list)
+-        elif o in ("-n", "--show_sockets"):
+-            show_sockets = True
+-        elif o in ("-m", "--move", "-x", "--spread"):
+-            spread = o in ("-x", "--spread")
+-            if not cpu_list:
+-                print("tuna: %s " % ("--spread" if spread else "--move") + _("requires a cpu list!"))
+-                sys.exit(2)
+-            if not (thread_list or irq_list):
+-                print("tuna: %s " % ("--spread" if spread else "--move") + _("requires a list of threads/irqs!"))
+-                sys.exit(2)
+-
+-            if thread_list:
+-                tuna.move_threads_to_cpu(cpu_list, thread_list, spread=spread)
+-
+-            if irq_list:
+-                tuna.move_irqs_to_cpu(cpu_list, irq_list, spread=spread)
+-        elif o in ("-s", "--save"):
+-            save(cpu_list, thread_list, a)
+-        elif o in ("-S", "--sockets"):
+-            (op, a) = pick_op(a)
+-            sockets = list(a.split(','))
+-
+-            if not cpu_list:
+-                cpu_list = []
+-
+-            cpu_info = sysfs.cpus()
+-            op_list = []
+-            for socket in sockets:
+-                if socket not in cpu_info.sockets:
+-                    print("tuna: %s" %
+-                          (_("invalid socket %(socket)s sockets available: %(available)s") %
+-                           {"socket": socket,
+-                            "available": ",".join(list(cpu_info.sockets.keys()))}))
+-                    sys.exit(2)
+-                op_list += [int(cpu.name[3:])
+-                            for cpu in cpu_info.sockets[socket]]
+-            cpu_list = do_list_op(op, cpu_list, op_list)
+-        elif o in ("-K", "--no_kthreads"):
+-            kthreads = False
+-        elif o in ("-q", "--irqs"):
+-            (op, a) = pick_op(a)
+-            op_list = reduce(lambda i, j: i + j,
+-                             list(map(irq_mapper, list(set(a.split(","))))))
+-            irq_list = do_list_op(op, irq_list, op_list)
+-            # See comment above about thread_list_str
+-            if not op_list and isinstance(a, type('')):
+-                irq_list_str = do_list_op(op, irq_list_str, a.split(","))
+-            if not op:
+-                thread_list = []
+-            if not ps:
+-                ps = procfs.pidstats()
+-            if tuna.has_threaded_irqs(ps):
+-                for irq in irq_list:
+-                    irq_re = tuna.threaded_irq_re(irq)
+-                    irq_threads = ps.find_by_regex(irq_re)
+-                    if irq_threads:
+-                        # Change the affinity of the thread too
+-                        # as we can't rely on changing the irq
+-                        # affinity changing the affinity of the
+-                        # thread or vice versa. We need to change
+-                        # both.
+-                        thread_list += irq_threads
+-
+-        elif o in ("-U", "--no_uthreads"):
+-            uthreads = False
+-        elif o in ("-v", "--version"):
+-            print(version)
+-        elif o in ("-W", "--what_is"):
+-            if not thread_list:
+-                print("tuna: --what_is " + _("requires a thread list!"))
+-                sys.exit(2)
+-            for tid in thread_list:
+-                thread_help(tid)
+-        elif o in ("-r", "--run"):
+-            # If -p is set, it will be consumed. So, no backward compatible
+-            # error handling action must be taken.
+-            p_waiting_action = False
+-
+-            # pick_op() before run the command: to remove the prefix
+-            # + or - from command line.
+-            (op, a) = pick_op(a)
+-
+-            # In order to include the new process, it must run
+-            # the command first, and then get the list of pids,
+-            tuna.run_command(a, policy, rtprio, cpu_list)
+-
+-            op_list = reduce(lambda i, j: i + j,
+-                             list(map(thread_mapper, a.split(","))))
+-            op_list = list(set(op_list))
+-            thread_list = do_list_op(op, thread_list, op_list)
+-
+-            # Check if a process name was specified and no
+-            # threads was found, which would result in an empty
+-            # thread list, i.e. we would print all the threads
+-            # in the system when we should print nothing.
+-            if not op_list and isinstance(a, type('')):
+-                thread_list_str = do_list_op(op, thread_list_str, a.split(","))
+-            if not op:
+-                irq_list = None
+-
+-    # For backward compatibility: when -p used to be only an Action, it
+-    # used to exit(2) if no action was taken (i.e. if no threads_list
+-    # was set).
+-    if p_waiting_action:
+-        print(("tuna: -p ") + _("requires a thread list!"))
+-        sys.exit(2)
++        except ValueError as e:
++            print(e, "tuna: --logging requires valid logging level\n")
++            print("Valid log levels: NOTSET, DEBUG, INFO, WARNING, ERROR")
++            print("Log levels may be specified numerically (0-4)\n")
++
++    if 'irq_list' in vars(args):
++        ps = procfs.pidstats()
++        if tuna.has_threaded_irqs(ps):
++            for irq in args.irq_list:
++                irq_re = tuna.threaded_irq_re(irq)
++                irq_threads = ps.find_by_regex(irq_re)
++                if irq_threads:
++                    # Change the affinity of the thread too
++                    # as we can't rely on changing the irq
++                    # affinity changing the affinity of the
++                    # thread or vice versa. We need to change
++                    # both.
++                    if 'thread_list' in vars(args):
++                        args.thread_list += irq_threads
++
++    if 'nohz_full' in vars(args) and args.nohz_full:
++        args.cpu_list = nohz_full_to_cpu()
++
++    if args.command in ['apply', 'a']:
++        apply_config(args.profilename)
++
++    elif args.command in ['include', 'I']:
++        tuna.include_cpus(args.cpu_list, get_nr_cpus())
++
++    elif args.command in ['isolate', 'i']:
++        tuna.isolate_cpus(args.cpu_list, get_nr_cpus())
++
++    elif args.command in ['run', 'r']:
++
++        tuna.run_command(args.run_command, args.priority[0], args.priority[1], args.cpu_list, args.background)
++
++    elif args.command in ['priority', 'p']:
++
++        try:
++            tuna.threads_set_priority(args.thread_list, args.priority, args.affect_children)
++        except OSError as err:
++            print("tuna: %s" % err)
++            sys.exit(2)
++
++    elif args.command in ['show_configs']:
++        list_config()
++
++    elif args.command in ['show_threads']:
++        do_ps(args.thread_list, args.cpu_list, args.irq_list, args.uthreads,
++                args.kthreads, args.affect_children, args.show_sockets if "show_sockets" in args else None, args.cgroups)
++
++    elif args.command in ['show_irqs']:
++        show_irqs(args.irq_list, args.cpu_list)
++
++    elif args.command in ['move', 'm', 'spread', 'x']:
++        spread = args.command in ['spread', 'x']
++
++        if not (args.thread_list or args.irq_list):
++            parser.error("tuna: %s " % (args.command) + _("requires a thread/irq list!\n"))
++
++        if args.thread_list:
++            tuna.move_threads_to_cpu(args.cpu_list, args.thread_list, spread=spread)
++
++        if args.irq_list:
++            tuna.move_irqs_to_cpu(args.cpu_list, args.irq_list, spread=spread)
++
++    elif args.command in ['s', 'save']:
++        save(args.cpu_list, args.thread_list, args.filename)
++
++    elif args.command in ['W', 'what_is']:
++        for tid in args.thread_list:
++            thread_help(tid)
+ 
+-    if run_gui:
++    elif args.command in ['g', 'gui']:
+         try:
+             from tuna import tuna_gui
+         except ImportError:
+             # gui packages not installed
+             print(_('tuna: packages needed for the GUI missing.'))
+             print(_('      Make sure xauth, pygtk2-libglade are installed.'))
+-            usage()
++            parser.print_help()
+             return
+         except RuntimeError:
+             print("tuna: machine needs to be authorized via xhost or ssh -X?")
+             return
+ 
+         try:
+-            cpus_filtered = filter if cpu_list else []
+-            app = tuna_gui.main_gui(kthreads, uthreads, cpus_filtered, gui_refresh, disable_perf)
++            app = tuna_gui.main_gui(args.kthreads, args.uthreads, args.cpu_list, args.refresh, args.disable_perf)
+             app.run()
+         except KeyboardInterrupt:
+             pass
+diff --git a/tuna/tuna.py b/tuna/tuna.py
+index 31707c9cb69c..8650f11c81c3 100755
+--- a/tuna/tuna.py
++++ b/tuna/tuna.py
+@@ -618,7 +618,7 @@ def get_kthread_sched_tunings(proc=None):
+ 
+     return kthreads
+ 
+-def run_command(cmd, policy, rtprio, cpu_list):
++def run_command(cmd, policy, rtprio, cpu_list, background):
+     newpid = os.fork()
+     if newpid == 0:
+         cmd_list = shlex.split(cmd)
+@@ -642,7 +642,8 @@ def run_command(cmd, policy, rtprio, cpu_list):
+             print("tuna: %s" % err)
+             sys.exit(2)
+     else:
+-        os.waitpid(newpid, 0)
++        if not background:
++            os.waitpid(newpid, 0)
+ 
+ def generate_rtgroups(filename, kthreads, nr_cpus):
+     f = open(filename, "w")
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-Update-manpages-for-argparse-CLI-changes.patch b/SOURCES/tuna-Update-manpages-for-argparse-CLI-changes.patch
new file mode 100644
index 0000000..88275aa
--- /dev/null
+++ b/SOURCES/tuna-Update-manpages-for-argparse-CLI-changes.patch
@@ -0,0 +1,372 @@
+From 0681906e75e1c8166126bbfc2f3055e7507bfcb5 Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Mon, 31 Oct 2022 13:04:28 -0400
+Subject: [PATCH 2/2] tuna: Update manpages for argparse CLI changes
+
+Due to the introduction of a new commandline interface,
+update the tuna manpages.
+
+Update with new command line options.
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+- Slightly edited the description
+- Changed "seperated" to "separated"
+- Changed the "explains"  to "describes" (not part of Leah's patch)
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/docs/tuna.8 b/docs/tuna.8
+index 3a065562b00e..f50a8c2a0a16 100644
+--- a/docs/tuna.8
++++ b/docs/tuna.8
+@@ -9,100 +9,261 @@ This manual page explains the \fBtuna\fR program. The program can be used to cha
+ .PP 
+ \fBtuna\fP can change scheduling policy, scheduler priority and processor affinity for processes and process threads. \fBtuna\fR can also change the processor affinity for interrupts.
+ When \fBtuna\fR is invoked without any options it starts up in its graphical interface mode. This manual page explains only the command\-line options for \fBtuna\fR
+-.SH "OPTIONS"
+-\fBtuna\fR has both \fIaction\fR options and \fImodifier\fR options. Modifier options must be specified on the command\-line before the actions they are intended to modify. Any modifier option applies to following actions on the same command-line until it is over-ridden.
+-.IP \fIActions\fR
++.SH "GLOBAL OPTIONS"
+ .TP
+ \fB\-h\fR, \fB\-\-help\fR
+ Print a list of options. \fBtuna\fR will exit after this action, ignoring the remainder of the command-line.
+ .TP
+-\fB\-g\fR, \fB\-\-gui\fR
+-Start the GUI. Actions that follow this on the command-line will be processed without waiting for the GUI to complete.
++\fB\-v\fR, \fB\-\-version\fR
++Show version
+ .TP
+-\fB\-a\fR, \fB\-\-config_file_apply=profilename\fR
+-Apply changes described in profile
++\fB\-L\fR, \fB\-\-logging\fR=\fILOG-LEVEL\fR
++Log application details to file for given LOG-LEVEL
+ .TP
+-\fB\-l\fR, \fB\-\-config_file_list\fR
+-List preloaded profiles
++\fB\-D\fR, \fB\-\-debug\fR
++Print DEBUG level logging details to console
++.SH "COMMANDS"
+ .TP
+-\fB\-i\fR, \fB\-\-isolate\fR
+-Move all allowed threads and IRQs away from CPU\-LIST. Requires \fB\-c\fR or \fB-S\fR.
++\fBtuna isolate\fR
++usage: tuna-cmd.py isolate [-h] (-c CPU-LIST | -S CPU-SOCKET-LIST | -N)
++
++Move all allowed threads and IRQs away from CPU\-LIST. Requires \fB\-c\fR, \fB-S\fR, or \fB-N\fR.
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -c CPU-LIST, --cpus CPU-LIST
++                        CPU-LIST affected by commands
++  -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST
++                        CPU-SOCKET-LIST affected by commands
++  -N, --nohz_full       CPUs in nohz_full kernel command line will be affected
++                        by operations
+ .TP
+-\fB\-I\fR, \fB\-\-include\fR
+-Allow all allowed threads and IRQs to run on CPU\-LIST. Requires \fB\-c\fR or \fB-S\fR.
++\fBtuna include\fR
++usage: tuna-cmd.py include [-h] (-c CPU-LIST | -S CPU-SOCKET-LIST | -N)
++
++Allow all allowed threads and IRQs to run on CPU\-LIST. Requires \fB\-c\fR, \fB-S\fR, or \fB-N\fR.
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -c CPU-LIST, --cpus CPU-LIST
++                        CPU-LIST affected by commands
++  -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST
++                        CPU-SOCKET-LIST affected by commands
++  -N, --nohz_full       CPUs in nohz_full kernel command line will be affected
++                        by operations
+ .TP
+-\fB\-m\fR, \fB\-\-move\fR
+-Move selected entities to CPU\-LIST. Requires \fB\-c\fR and either \fB-t\fR or \fB-q\fR.
++\fBtuna move\fR
++usage: tuna-cmd.py move [-h] (-c CPU-LIST | -S CPU-SOCKET-LIST | -N)
++                        [-t THREAD-LIST] [-q IRQ-LIST]
++
++Move selected entities to CPU\-LIST. Requires \fB\-c\fR, \fB-S\fR, or \fB-N\fR and \fB-t\fR or \fB-q\fR.
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -c CPU-LIST, --cpus CPU-LIST
++                        CPU-LIST affected by commands
++  -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST
++                        CPU-SOCKET-LIST affected by commands
++  -N, --nohz_full       CPUs in nohz_full kernel command line will be affected
++                        by operations
++  -t THREAD-LIST, --threads THREAD-LIST
++                        THREAD-LIST affected by commands
++  -q IRQ-LIST, --irqs IRQ-LIST
++                        IRQ-LIST affect by commands
+ .TP
+-\fB\-p\fR, \fB\-\-priority\fR=\fI[POLICY:]\fRRTPRIO
+-Set thread scheduler tunables: POLICY and RTPRIO. POLICY is one of OTHER, FIFO, RR, or BATCH. If \fB\-t\fR is not set, \fB\-p\fR will act as a Modifier saving the sched tunables for future Actions, for example \fB\-r\fR.
++\fBtuna spread\fR
++usage: tuna-cmd.py spread [-h] (-c CPU-LIST | -S CPU-SOCKET-LIST | -N)
++                          [-t THREAD-LIST] [-q IRQ-LIST]
++
++Spread selected entities over CPU-LIST. The specified threads and IRQs are each assigned to one cpu in CPU-LIST. Requires \fB\-c\fR, \fB-S\fR, or \fB-N\fR and \fB-t\fR or \fB-q\fR.
++
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -c CPU-LIST, --cpus CPU-LIST
++                        CPU-LIST affected by commands
++  -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST
++                        CPU-SOCKET-LIST affected by commands
++  -N, --nohz_full       CPUs in nohz_full kernel command line will be affected
++                        by operations
++  -t THREAD-LIST, --threads THREAD-LIST
++                        THREAD-LIST affected by commands
++  -q IRQ-LIST, --irqs IRQ-LIST
++                        IRQ-LIST affect by commands
+ .TP
+-\fB\-P\fR, \fB\-\-show_threads\fR
+-Show thread list.
++
++\fBtuna priority\fR
++usage: tuna-cmd.py priority [-h] -t THREAD-LIST [-C] POLICY:RTPRIO
++
++Set thread scheduler tunables: POLICY and RTPRIO. POLICY is one of OTHER, FIFO, RR, or BATCH. Provide POLICY, RTPRIO, or POLICY:RTPRIO separated by ":". If only POLICY is set, the RT priority will default to 1 if the policy is RT, and 0 otherwise. If only RTPRIO is specified, policy will not be changed.
++
++positional arguments:
++  POLICY:RTPRIO         Set thread scheduler tunables: POLICY and RTPRIO
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -t THREAD-LIST, --threads THREAD-LIST
++                        THREAD-LIST affected by commands
++  -C, --affect_children
++                        Operation will affect children threads
+ .TP
+-\fB\-Q\fR, \fB\-\-show_irqs\fR
+-Show IRQ list.
++\fBtuna run\fR
++usage: tuna-cmd.py run [-h] [-c CPU-LIST | -S CPU-SOCKET-LIST | -N]
++                       [-p PRIORITY] [-b]
++                       COMMAND
++
++Run the COMMAND. The entire command line must be provided inside "quotes". Modifiers \fB-c\fR, \fB-S\fR and \fB-p\fR can be used to set the affinity and scheduler tunables of the given COMMAND.
++
++positional arguments:
++  COMMAND               fork a new process and run the "COMMAND"
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -c CPU-LIST, --cpus CPU-LIST
++                        CPU-LIST affected by commands
++  -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST
++                        CPU-SOCKET-LIST affected by commands
++  -N, --nohz_full       CPUs in nohz_full kernel command line will be affected
++                        by operations
++  -p PRIORITY, --priority PRIORITY
++                        Set thread scheduler tunables: POLICY and RTPRIO
++  -b, --background      Run command as background task
++
+ .TP
+-\fB\-r\fR, \fB\-\-run\fR=\fI"COMMAND"\fR
+-Run the COMMAND. If arguments are passed, the entire command line must be provided inside "quotes". Modifiers \fB-c\fR and \fB-p\fR can be used to set the affinity and scheduler tunables of the given COMMAND. The arg[0] (i.e. the command) will be set in THREAD\-LIST. Likewise the \fB-t\fR, the COMMAND accepts the prefix \fB+\fR and \fB-\fR as wildcards in order to be appended or removed from THREAD\-LIST, respectively.
++\fBtuna save\fR
++usage: tuna-cmd.py save [-h] [-c CPU-LIST | -S CPU-SOCKET-LIST | -N]
++                        [-t THREAD-LIST]
++                        FILENAME
++
++Save kthreads sched tunables to FILENAME
++
++positional arguments:
++  FILENAME              Save kthreads sched tunables to this file
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -c CPU-LIST, --cpus CPU-LIST
++                        CPU-LIST affected by commands
++  -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST
++                        CPU-SOCKET-LIST affected by commands
++  -N, --nohz_full       CPUs in nohz_full kernel command line will be affected
++                        by operations
++  -t THREAD-LIST, --threads THREAD-LIST
++                        THREAD-LIST affected by commands
+ .TP
+-\fB\-R\fR, \fB\-\-refresh\fR=\fIMSEC\fR
+-Refresh the GUI every MSEC milliseconds
++\fBtuna apply\fR
++usage: tuna-cmd.py apply [-h] profilename
++
++Apply changes described in profile
++
++positional arguments:
++  profilename  Apply changes described in this file
++
++optional arguments:
++  -h, --help   show this help message and exit
++
+ .TP
+-\fB\-s\fR, \fB\-\-save\fR=\fIFILENAME\fR
+-Save kthreads sched tunables to FILENAME.
++\fBtuna show_threads\fR
++usage: tuna-cmd.py show_threads [-h] [-c CPU-LIST | -N | -S CPU-SOCKET-LIST]
++                                [-t THREAD-LIST | -q IRQ-LIST] [-U] [-K] [-C]
++                                [-G]
++
++Show thread list
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -c CPU-LIST, --cpus CPU-LIST
++                        CPU-LIST affected by commands
++  -N, --nohz_full       CPUs in nohz_full kernel command line will be affected
++                        by operations
++  -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST
++                        CPU-SOCKET-LIST affected by commands
++  -t THREAD-LIST, --threads THREAD-LIST
++                        THREAD-LIST affected by commands
++  -q IRQ-LIST, --irqs IRQ-LIST
++                        IRQ-LIST affect by commands
++  -U, --no_uthreads     Operations will not affect user threads
++  -K, --no_kthreads     Operations will not affect kernel threads
++  -C, --affect_children
++                        Operation will affect children threads
++  -G, --cgroups         Display the processes with the type of cgroups they
++                        are in
++
+ .TP
+-\fB\-v\fR, \fB\-\-version\fR
+-Show version
++\fBtuna show_irqs\fR
++usage: tuna-cmd.py show_irqs [-h] [-c CPU-LIST | -N | -S CPU-SOCKET-LIST]
++                             [-q IRQ-LIST]
++
++Show IRQ list
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -c CPU-LIST, --cpus CPU-LIST
++                        CPU-LIST affected by commands
++  -N, --nohz_full       CPUs in nohz_full kernel command line will be affected
++                        by operations
++  -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST
++                        CPU-SOCKET-LIST affected by commands
++  -q IRQ-LIST, --irqs IRQ-LIST
++                        IRQ-LIST affect by commands
++
+ .TP
+-\fB\-W\fR, \fB\-\-what_is\fR
+-Provides help about selected entities. Requires \fB-t\fR.
++\fBtuna show_configs\fR
++usage: tuna-cmd.py show_configs [-h]
++
++List preloaded profiles
++
++optional arguments:
++  -h, --help  show this help message and exit
++
+ .TP
+-\fB\-x\fR, \fB\-\-spread\fR
+-Spread selected entities over CPU\-LIST. Requires at least one of \fB-t\fR or \fB-q\fR. The specified threads and IRQs are each assigned to one cpu in CPU\-LIST.
++\fB tuna what_is\fR
++usage: tuna-cmd.py what_is [-h] THREAD-LIST
++
++Provides help about selected entities
++
++positional arguments:
++  THREAD-LIST  THREAD-LIST affected by commands
++
++optional arguments:
++  -h, --help   show this help message and exit
++.TP
++\fB tuna gui\fR
++usage: tuna-cmd.py gui [-h] [-d] [-R MSEC]
++                       [-c CPU-LIST | -N | -S CPU-SOCKET-LIST] [-U] [-K]
++
++Start the GUI
++
++optional arguments:
++  -h, --help            show this help message and exit
++  -d, --disable_perf    Explicitly disable usage of perf in GUI for process
++                        view
++  -R MSEC, --refresh MSEC
++                        Refresh the GUI every MSEC milliseconds
++  -c CPU-LIST, --cpus CPU-LIST
++                        CPU-LIST affected by commands
++  -N, --nohz_full       CPUs in nohz_full kernel command line will be affected
++                        by operations
++  -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST
++                        CPU-SOCKET-LIST affected by commands
++  -U, --no_uthreads     Operations will not affect user threads
++  -K, --no_kthreads     Operations will not affect kernel threads
+ .IP \fIModifiers\fR
+ .TP
+ \fB\-c\fR, \fB\-\-cpus\fR=\fICPU\-LIST\fR
+-CPU\-LIST affected by commands. Requires a CPU number or a comma-separated list of CPU numbers.
+-.TP
+-\fB\-C\fR, \fB\-\-affect_children\fR
+-Operation will affect children threads.
+-.TP
+-\fB\-d\fR, \fB\-\-disable_perf\fR
+-Explicitly disable usage of perf in GUI for process view
+-.TP
+-\fB\-D\fR, \fB\-\-debug\fR
+-Print DEBUG level logging details to console.
+-.TP
+-\fB\-f\fR, \fB\-\-filter\fR
+-Disable display of selected CPUs in \fB--gui\fR. Requires \fB-c\R.
+-.TP
+-\fB\-G\fR, \fB\-\-cgroup\fR
+-Display the processes with the type of cgroups they are in. Requires \fB-P\R.
+-.TP
+-\fB\-K\fR, \fB\-\-no_kthreads\fR
+-Operations will not affect kernel threads.
+-.TP
+-\fB\-L\fR, \fB\-\-logging\fR=\fILOG\-LEVEL\fR
+-Log application details to log file for given LOG-LEVEL
+-.TP
+-\fB\-N\fR, \fB\-\-nohz_full\fR
+-CPUs in nohz_full= kernel command line parameter will be affected by operations
+-.TP
+-\fB\-q\fR, \fB\-\-irqs\fR=\fIIRQ\-LIST\fR
+-IRQ\-LIST affected by commands. Requires an IRQ number or a comma-separated list of IRQ numbers.
++CPU\-LIST affected by commands. Requires a CPU number, a range, or a comma-separated list of CPU numbers.
+ .TP
+ \fB\-S\fR, \fB\-\-sockets\fR=\fICPU\-SOCKET\-LIST\fR
+ CPU\-SOCKET\-LIST affected by commands. Requires a socket number or a comma-separated list of socket numbers.
+ .TP
+ \fB\-t\fR, \fB\-\-threads\fR=\fITHREAD\-LIST\fR
+-THREAD\-LIST affected by commands. Requires a thread number or thread name, or a comma-separated list of thread numbers and/or names. Thread names may contain wildcards. Be sure to quote or escape any wildcard specifications. If only \fB-\fR is passed as argument, the THREAD\-LIST will be cleared.
++THREAD\-LIST affected by commands. Requires a thread number or thread name, or a comma-separated list of thread numbers and/or names. Thread names may contain wildcards. Be sure to quote or escape any wildcard specifications.
+ .TP
+-\fB\-U\fR, \fB\-\-no_uthreads\fR
+-Operations will not affect user threads.
+ .SH USAGE EXAMPLES
+ If for instance the Ethernet NICs have multiple queues for both receive and transmit, each with its own IRQ, the Ethernet IRQs can be associated with a CPU socket:
+ .TP
+-.B tuna -S 2 -i -q 'eth*' -x
++.B tuna isolate -S 2
++.TP
++.B tuna spread -q 'eth*' -S 2
+ 
+ Move everything off the CPUs in socket 2, then spread the IRQs for the Ethernet devices across those same CPUs.
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index 4ac01f4f4522..8be35f7fb4c4 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -101,7 +101,7 @@ def gen_parser():
+             "filename": dict(metavar='FILENAME', type=str, help="Save kthreads sched tunables to this file"),
+             "profilename": dict(type=str, help="Apply changes described in this file"),
+             "run_command": dict(metavar='COMMAND', type=str, help="fork a new process and run the \"COMMAND\""),
+-            "priority": dict(type=tuna.get_policy_and_rtprio, help="policy/priority help"),
++            "priority": dict(type=tuna.get_policy_and_rtprio, metavar="POLICY:RTPRIO", help="Set thread scheduler tunables: POLICY and RTPRIO"),
+           }
+ 
+     MODS = {
+@@ -120,7 +120,7 @@ def gen_parser():
+             "no_kthreads": dict(action='store_false', dest='kthreads', help="Operations will not affect kernel threads"),
+             "disable_perf": dict(action='store_true', help="Explicitly disable usage of perf in GUI for process view"),
+             "refresh": dict(default=2500, metavar='MSEC', type=int, help="Refresh the GUI every MSEC milliseconds"),
+-            "priority": dict(default=(None, None), type=tuna.get_policy_and_rtprio, help="Set thread scheduler tunables: POLICY and RTPRIO"),
++            "priority": dict(default=(None, None), metavar="POLICY:RTPRIO", type=tuna.get_policy_and_rtprio, help="Set thread scheduler tunables: POLICY and RTPRIO"),
+             "background": dict(action='store_true', help="Run command as background task")
+          }
+ 
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-remove-import-and-fix-help-message.patch b/SOURCES/tuna-remove-import-and-fix-help-message.patch
new file mode 100644
index 0000000..0bd2812
--- /dev/null
+++ b/SOURCES/tuna-remove-import-and-fix-help-message.patch
@@ -0,0 +1,33 @@
+From bef2c2009ed5151cf7ae4bf258a908c9f577ca7d Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Wed, 2 Nov 2022 11:16:48 -0400
+Subject: [PATCH 1/2] tuna: remove import and fix help message
+
+Remove getopt import and fix help message for 'spread' command.
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index 44d9faaf15fb..4ac01f4f4522 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -21,7 +21,6 @@ import os
+ import sys
+ import errno
+ import re
+-import getopt
+ import fnmatch
+ import gettext
+ import locale
+@@ -140,7 +139,7 @@ def gen_parser():
+                                      help="Allow all threads to run on CPU-LIST")
+     move = subparser.add_parser('move', description="Move selected entities to CPU-LIST",
+                                     help="Move selected entities to CPU-LIST")
+-    spread = subparser.add_parser('spread', description="Move selected entities to CPU-LIST",
++    spread = subparser.add_parser('spread', description="Spread selected entities to CPU-LIST",
+                                     help="Spread selected entities over CPU-LIST")
+     priority = subparser.add_parser('priority', description="Set thread scheduler tunables: POLICY and RTPRIO",
+                                     help="Set thread scheduler tunables: POLICY and RTPRIO")
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-tuna-cmd.py-use-fstrings.patch b/SOURCES/tuna-tuna-cmd.py-use-fstrings.patch
new file mode 100644
index 0000000..e9ba212
--- /dev/null
+++ b/SOURCES/tuna-tuna-cmd.py-use-fstrings.patch
@@ -0,0 +1,75 @@
+From 1e2f6a3b64bce4ea335e97b1ab31ada3e082a6bc Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Tue, 1 Nov 2022 13:24:00 -0400
+Subject: [PATCH 2/4] tuna: tuna-cmd.py use fstrings
+
+Add fstrings where possible to improve readabilty
+
+Due to the discussion regarding dropping the language feature, gettext
+shorthand _() have been removed.
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/tuna-cmd.py b/tuna-cmd.py
+index c5bc65059da7..44d9faaf15fb 100755
+--- a/tuna-cmd.py
++++ b/tuna-cmd.py
+@@ -89,7 +89,7 @@ irqs = None
+ 
+ class HelpMessageParser(argparse.ArgumentParser):
+     def error(self, message):
+-        sys.stderr.write('error: %s\n' % message)
++        sys.stderr.write(f'error: {message}\n')
+         self.print_help()
+         sys.exit(2)
+ 
+@@ -263,13 +263,16 @@ def thread_help(tid):
+         ps = procfs.pidstats()
+ 
+     if tid not in ps:
+-        print("tuna: " + _("thread %d doesn't exists!") % tid)
++        print(f"tuna: thread {tid} doesn't exist!")
+         return
+ 
+     pinfo = ps[tid]
+     cmdline = procfs.process_cmdline(pinfo)
+     help, title = tuna.kthread_help_plain_text(tid, cmdline)
+-    print("%s\n\n%s" % (title, _(help)))
++    print(title, "\n\n")
++    if help.isspace():
++        help = "No help description available."
++    print(help)
+ 
+ 
+ def save(cpu_list, thread_list, filename):
+@@ -295,7 +298,7 @@ def ps_show_header(has_ctxt_switch_info, cgroups=False):
+ 
+ def ps_show_sockets(pid, ps, inodes, inode_re, indent=0):
+     header_printed = False
+-    dirname = "/proc/%s/fd" % pid
++    dirname = f"/proc/{pid}/fd"
+     try:
+         filenames = os.listdir(dirname)
+     except:  # Process died
+@@ -688,7 +691,7 @@ def main():
+         try:
+             tuna.threads_set_priority(args.thread_list, args.priority, args.affect_children)
+         except OSError as err:
+-            print("tuna: %s" % err)
++            print(f"tuna: {err}")
+             sys.exit(2)
+ 
+     elif args.command in ['show_configs']:
+@@ -705,7 +708,7 @@ def main():
+         spread = args.command in ['spread', 'x']
+ 
+         if not (args.thread_list or args.irq_list):
+-            parser.error("tuna: %s " % (args.command) + _("requires a thread/irq list!\n"))
++            parser.error(f"tuna: {args.command} requires a thread/irq list!\n")
+ 
+         if args.thread_list:
+             tuna.move_threads_to_cpu(args.cpu_list, args.thread_list, spread=spread)
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-tuna.py-use-fstrings.patch b/SOURCES/tuna-tuna.py-use-fstrings.patch
new file mode 100644
index 0000000..02277c9
--- /dev/null
+++ b/SOURCES/tuna-tuna.py-use-fstrings.patch
@@ -0,0 +1,94 @@
+From efcc75a317b63227a8630ca17e9bcf153a6cc03e Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Mon, 31 Oct 2022 13:11:31 -0400
+Subject: [PATCH 3/4] tuna: tuna.py use fstrings
+
+Add fstrings where possible to improve readabilty
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/tuna/tuna.py b/tuna/tuna.py
+index 43adb84079e4..e527facb151c 100755
+--- a/tuna/tuna.py
++++ b/tuna/tuna.py
+@@ -58,7 +58,7 @@ def iskthread(pid):
+     # in this function, so that they know that the thread vanished and
+     # can act accordingly, removing entries from tree views, etc
+     try:
+-        f = open("/proc/%d/smaps" % pid)
++        f = open(f"/proc/{pid}/smaps")
+     except IOError:
+         # Thread has vanished
+         return True
+@@ -88,7 +88,7 @@ def is_irq_thread(cmd):
+     return cmd[:4] in ("IRQ-", "irq/")
+ 
+ def threaded_irq_re(irq):
+-    return re.compile("(irq/%s-.+|IRQ-%s)" % (irq, irq))
++    return re.compile(f"(irq/{irq}-.+|IRQ-{irq})")
+ 
+ # FIXME: Move to python-linux-procfs
+ def has_threaded_irqs(ps):
+@@ -96,10 +96,10 @@ def has_threaded_irqs(ps):
+     return len(ps.find_by_regex(irq_re)) > 0
+ 
+ def set_irq_affinity_filename(filename, bitmasklist):
+-    pathname = "/proc/irq/%s" % filename
++    pathname = f"/proc/irq/{filename}"
+     f = open(pathname, "w")
+     text = ",".join(["%x" % a for a in bitmasklist])
+-    f.write("%s\n" % text)
++    f.write(f"{text}\n")
+     try:
+         f.close()
+     except IOError:
+@@ -225,7 +225,7 @@ def move_threads_to_cpu(cpus, pid_list, set_affinity_warning=None, spread=False)
+             if pid not in ps:
+                 continue
+ 
+-            threads = procfs.pidstats("/proc/%d/task" % pid)
++            threads = procfs.pidstats(f"/proc/{pid}/task")
+             for tid in list(threads.keys()):
+                 try:
+                     curr_affinity = os.sched_getaffinity(tid)
+@@ -320,11 +320,11 @@ def affinity_remove_cpus(affinity, cpus, nr_cpus):
+ # Should be moved to python_linux_procfs.interrupts, shared with interrupts.parse_affinity, etc.
+ def parse_irq_affinity_filename(filename, nr_cpus):
+     try:
+-        f = open("/proc/irq/%s" % filename)
++        f = open(f"/proc/irq/{filename}")
+     except IOError as err:
+         if procfs.is_s390():
+             print("This operation is not supported on s390", file=sys.stderr)
+-            print("tuna: %s" % err, file=sys.stderr)
++            print(f"tuna: {err}", file=sys.stderr)
+             sys.exit(2)
+ 
+     line = f.readline()
+@@ -624,19 +624,19 @@ def run_command(cmd, policy, rtprio, cpu_list, background):
+             try:
+                 thread_set_priority(pid, policy, rtprio)
+             except (SystemError, OSError) as err:
+-                print("tuna: %s" % err)
++                print(f"tuna: {err}")
+                 sys.exit(2)
+         if cpu_list:
+             try:
+                 os.sched_setaffinity(pid, cpu_list)
+             except (SystemError, OSError) as err:
+-                print("tuna: %s" % err)
++                print(f"tuna: {err}")
+                 sys.exit(2)
+ 
+         try:
+             os.execvp(cmd_list[0], cmd_list)
+         except (SystemError, OSError) as err:
+-            print("tuna: %s" % err)
++            print(f"tuna: {err}")
+             sys.exit(2)
+     else:
+         if not background:
+-- 
+2.31.1
+
diff --git a/SOURCES/tuna-tuna_gui.py-use-fstrings.patch b/SOURCES/tuna-tuna_gui.py-use-fstrings.patch
new file mode 100644
index 0000000..9e4663e
--- /dev/null
+++ b/SOURCES/tuna-tuna_gui.py-use-fstrings.patch
@@ -0,0 +1,26 @@
+From 67c5a4dc31f7dd8a6204a1a636fcec4e8f17a86b Mon Sep 17 00:00:00 2001
+From: Leah Leshchinsky <lleshchi@redhat.com>
+Date: Fri, 28 Oct 2022 12:52:46 -0400
+Subject: [PATCH 1/4] tuna: tuna_gui.py use fstrings
+
+Add fstrings where possible to improve readabilty
+
+Signed-off-by: Leah Leshchinsky <lleshchi@redhat.com>
+Signed-off-by: John Kacur <jkacur@redhat.com>
+
+diff --git a/tuna/tuna_gui.py b/tuna/tuna_gui.py
+index f1f2caacbcba..459f90303ed5 100755
+--- a/tuna/tuna_gui.py
++++ b/tuna/tuna_gui.py
+@@ -33,7 +33,7 @@ class main_gui:
+         if self.check_root():
+             sys.exit(1)
+         for dir in tuna_glade_dirs:
+-            tuna_glade = "%s/tuna_gui.glade" % dir
++            tuna_glade = f"{dir}/tuna_gui.glade"
+             if os.access(tuna_glade, os.F_OK):
+                 break
+         self.wtree = Gtk.Builder()
+-- 
+2.31.1
+
diff --git a/SPECS/tuna.spec b/SPECS/tuna.spec
index d885bf6..7e44416 100644
--- a/SPECS/tuna.spec
+++ b/SPECS/tuna.spec
@@ -1,6 +1,6 @@
 Name: tuna
 Version: 0.18
-Release: 2%{?dist}
+Release: 12%{?dist}
 License: GPLv2
 Summary: Application tuning GUI & command line utility
 URL: https://git.kernel.org/pub/scm/utils/tuna/tuna.git
@@ -8,12 +8,24 @@ Source: https://www.kernel.org/pub/software/utils/%{name}/%{name}-%{version}.tar
 
 BuildArch: noarch
 BuildRequires: python3-devel, gettext
-Requires: python3-ethtool
 Requires: python3-linux-procfs >= 0.6
 # This really should be a Suggests...
 # Requires: python-inet_diag
 
 # Patches
+Patch1: tuna-Update-command-line-interface.patch
+Patch2: tuna-Move-get_policy_and_rtprio-call-to-parser-level.patch
+Patch3: tuna-Add-sockets-command-line-option.patch
+Patch4: tuna-Replace-python_ethtool-with-builtin-funtionalit.patch
+Patch5: tuna-Fix-matching-irqs-in-ps_show_thread.patch
+Patch6: tuna-Remove-threads-print-statement.patch
+Patch7: tuna-tuna_gui.py-use-fstrings.patch
+Patch8: tuna-tuna-cmd.py-use-fstrings.patch
+Patch9: tuna-tuna.py-use-fstrings.patch
+Patch10: tuna-remove-import-and-fix-help-message.patch
+Patch11: tuna-Update-manpages-for-argparse-CLI-changes.patch
+Patch12: tuna-Adapt-show_threads-cgroup-output-to-terminal-si.patch
+Patch13: tuna-Fix-show_threads-cgroups-run-without-a-term.patch
 
 %description
 Provides interface for changing scheduler and IRQ tunables, at whole CPU and at
@@ -25,7 +37,7 @@ Can be used as a command line utility without requiring the GUI libraries to be
 installed.
 
 %prep
-%setup -q
+%autosetup -p1
 
 %build
 %py3_build
@@ -67,6 +79,49 @@ done
 %{_datadir}/polkit-1/actions/org.tuna.policy
 
 %changelog
+* Wed Nov 23 2022 Leah Leshchinsky <lleshchi@redhat.com> - 0.18-12
+- Fix show_threads --cgroups run without term
+Resolves: rhbz#2121517
+
+* Fri Nov 18 2022 Leah Leshchinsky <lleshchi@redhat.com> - 0.18-11
+- Adapt show_threads cgroup output to terminal size
+Resolves: rhbz#2121517
+
+* Wed Nov 09 2022 Leah Leshchinsky <lleshchi@redhat.com> - 0.18-10
+- Edit run_tests.sh to support new CLI changes
+Resolves: rhbz#2141349
+
+* Tue Nov 08 2022 Leah Leshchinsky <lleshchi@redhat.com> - 0.18-9
+- Remove import and fix help message
+- Update manpages for argparse CLI changes
+Resolves: rhbz#2138692
+
+* Wed Nov 02 2022 Leah Leshchinsky <lleshchi@redhat.com> - 0.18-8
+- Use f-strings in tuna where possible
+Resolves: rhbz#2120803
+
+* Wed Oct 26 2022 Leah Leshchinsky <lleshchi@redhat.com> - 0.18-7
+- Add sockets command line option
+Resolves: rhbz#2122781
+
+* Wed Oct 26 2022 Leah Leshchinsky <lleshchi@redhat.com> - 0.18-6
+- Update tuna command line interface
+- Move get_policy_and_rtprio call to parser level
+- Remove threads print statement
+Resolves: rhbz#2062865
+
+* Mon Oct 03 2022 John Kacur <jkacur@redhat.com> - 0.18-5
+- Match irqs with "irqs/"
+Resolves: rhbz#2131343
+
+* Fri Sep 30 2022 John Kacur <jkacur@redhat.com> - 0.18-4
+- Remove the "Requires" of python-ethtool from the specfile
+Resolves: rhbz#2123751
+
+* Fri Sep 30 2022 John Kacur <jkacur@redhat.com> - 0.18-3
+- Replace dependency on python-ethtool with built-in functionality
+Resolves: rhbz#2123751
+
 * Wed Jun 29 2022 Leah Leshchinsky <lleshchi@redhat.com> - 0.18-2
 - Delete patches
 Resolves: rhbz#2068629