|
Server : Apache System : Linux ecngx264.inmotionhosting.com 4.18.0-553.77.1.lve.el8.x86_64 #1 SMP Wed Oct 8 14:21:00 UTC 2025 x86_64 User : lonias5 ( 3576) PHP Version : 7.3.33 Disable Function : NONE Directory : /proc/self/root/proc/thread-self/root/opt/sharedrads/ |
Upload File : |
#!/usr/lib/rads/venv/bin/python3
import argparse
import re
import subprocess
from collections import defaultdict
from datetime import datetime, timedelta
from prettytable import PrettyTable
from rads import is_cpuser, get_plan
def parse_args():
parser = argparse.ArgumentParser(
description="Process pacct files and total CPU time for cPanel users."
)
parser.add_argument(
"--days",
type=int,
default=10,
help="Number of pacct files to process (default: 10)",
)
parser.add_argument(
"-l",
"--limit-users",
type=int,
default=5,
help="Number users to display (default: 5)",
)
parser.add_argument(
"--date",
type=str,
default=datetime.today().strftime("%Y-%m-%d"),
help="Start date in format YYYY-MM-DD (default: today)",
)
return parser.parse_args()
def get_pacct_files(start_date: str, days: int):
"""
Generate a list of process accounting (pacct) file paths
for a given number of days.
Args:
start_date (str): The start date in 'YYYY-MM-DD' format.
days (int): The number of days of pacct files to retrieve.
Returns:
list[str]: A list of file paths to the compressed pacct
logs in `/var/account/`, named in the format
`pacct-YYYYMMDD.gz`.
Raises:
ValueError: If `start_date` is not in the correct 'YYYY-MM-DD' format.
Notes:
- If `start_date` is today, the function returns files from
the previous `days` days.
- Otherwise, it returns files counting backward from the given
`start_date`.
"""
try:
start = datetime.strptime(start_date, "%Y-%m-%d")
except ValueError as exc:
raise ValueError("Date must be in YYYY-MM-DD format.") from exc
today = datetime.today().date()
start_date_obj = start.date()
files = []
if start_date_obj == today:
for i in range(1, days + 1):
day = today - timedelta(days=i)
files.append(f"/var/account/pacct-{day.strftime('%Y%m%d')}.gz")
else:
for i in range(days):
day = start_date_obj - timedelta(days=i)
files.append(f"/var/account/pacct-{day.strftime('%Y%m%d')}.gz")
return files
def process_pacct_file(file_path):
"""
Run 'zcat' on a compressed pacct file and pipe the output to 'sa -cmi -'.
Returns the output lines from the 'sa' command as a list of strings.
"""
try:
with subprocess.Popen(
["zcat", file_path], stdout=subprocess.PIPE
) as zcat_proc:
with subprocess.Popen(
["sa", "-cmi", "-"],
stdin=zcat_proc.stdout,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
) as sa_proc:
zcat_proc.stdout.close()
stdout, stderr = sa_proc.communicate()
if sa_proc.returncode != 0:
print("Error running sa:", stderr)
return []
return stdout.splitlines()
except Exception as e:
print(f"Failed to process {file_path}: {str(e)}")
return []
def main():
args = parse_args()
pacct_files = get_pacct_files(args.date, args.days)
sa_cp_data = defaultdict(float)
sa_proc_data = defaultdict(int)
sa_top_user = defaultdict(int)
for file_path in pacct_files:
pacct_data = process_pacct_file(file_path)
try:
i = 1
for line in pacct_data:
columns = re.split(r"\s+", line.strip())
if is_cpuser(columns[0]):
try:
username = columns[0]
num_proc = int(columns[1])
cpu_time = float(columns[5].replace("cp", ""))
sa_cp_data[username] += cpu_time
sa_proc_data[username] += num_proc
if i <= args.limit_users:
sa_top_user[username] += 1
i += 1
except ValueError:
continue
except Exception as e:
print(f"Failed to process {file_path}: {str(e)}")
top_users = sorted(sa_cp_data.items(), key=lambda x: x[1], reverse=True)
table = PrettyTable()
table.field_names = [
"Username",
"Total CPU",
"Average CPU/day",
"Total Procs",
"Average Procs/day",
f"Days in top {args.limit_users}",
"Plan",
]
num_days = len(pacct_files)
for (
username,
total_cpu_time,
) in top_users[: args.limit_users]:
table.add_row(
[
username,
f"{total_cpu_time:.2f}cp",
f"{(total_cpu_time / num_days):.2f}cp",
f"{sa_proc_data[username]}",
f"{(sa_proc_data[username] / num_days):.2f}",
f"{sa_top_user[username]}/{num_days}",
get_plan(username),
]
)
print(table)
if __name__ == "__main__":
main()