Mini Shell
Direktori : /opt/sharedrads/ |
|
Current File : //opt/sharedrads/check_lve |
#!/opt/imh-python/bin/python3
"""Generate a chart of resource usage as reported by lveinfo. This script can
also generate a resource usage chart for user review."""
from rads import prompt_y_n, is_cpuser
from rads.color import red, green
from collections import OrderedDict
from prettytable import PrettyTable
from pwd import getpwuid, getpwnam
from grp import getgrnam
import subprocess
import argparse
import json
import sys
import os
USERCOUNT = 15
def parse_args():
"""Get command line arguments"""
parser = argparse.ArgumentParser(description=__doc__)
# fmt: off
group = parser.add_mutually_exclusive_group(required=True)
# mutually exclusive arguments
group.add_argument(
"-c", "--cpu", action="store_true", dest="CPU",
help="Show and sort by CPU usage",
)
group.add_argument(
"-m", "--memory", action="store_true", dest="Mem",
help="Show and sort by memory (physical and virtual) usage",
)
group.add_argument(
"-p", "--procs", action="store_true", dest="Proc",
help="Show and sort by processes",
)
group.add_argument(
"-i", "--io", action="store_true", dest="IO",
help="Show and sort by IO and IO Operations",
)
group.add_argument(
"-f", "--faults", action="store_true", dest="Fault",
help="Show and sort by cpu, proc, mem, and IO faults.",
)
group.add_argument(
"--chart", action="store_true", dest="Chart",
help="Generate a chart for the given user in --location",
)
# additional arguments (location and user)
parser.add_argument(
"-l", "--location", action="store", dest="Location", nargs="?",
help="Designate the location for the chart image generated "
"e.g. /home/userna5/lvechart.9-30.png",
)
parser.add_argument(
"-u", "--user", action="store", dest="User", nargs="?",
help="User for chart generation",
)
parser.add_argument(
"-t", "--time", action="store", dest="Time", nargs="?", default="5m",
help="time period; specify minutes (m), hours (h), days (d), today, "
"yesterday; 5m - last 5 minutes, 4h - last four hours, 2d - last "
"2 days including today",
)
# fmt: on
# get the args
results = parser.parse_args()
# if a location is provided, we probably meant to --chart
if results.Location and not results.Chart:
print("Assuming --chart")
results.Chart = True
# if --chart but no --user or --location
if results.Chart and (not results.User or not results.Location):
print(red("--chart requires a user and a location."))
sys.exit(1)
# check to make sure User is an actual cPanel user
if results.User and not is_cpuser(results.User):
print(red(f"{results.User} is not a valid cPanel user."))
sys.exit(1)
# make sure the path to --location exists
if results.Location and not os.path.exists(
os.path.dirname(results.Location)
):
print(
red(os.path.dirname(results.Location)),
red("is not a valid path. Does it exist?"),
)
sys.exit(1)
# get the owner of the --location dir
if results.Location:
owner = getpwuid(
os.stat(os.path.dirname(results.Location)).st_uid
).pw_name
# is the --location owned by --user? If not, verify this is okay
if results.User and results.User != owner:
# The user and location owner don't match. Prompt to continue
if not prompt_y_n(
f"{owner} is the owner of {results.Location} but you entered "
f"--user {results.User}. Proceed?"
):
print("Exiting.")
sys.exit(1)
return results
def generate_chart(user, location):
"""Generate the LVE chart in the provided location
lvechart --period=5d --user=$USER --output=$LOCATION"""
userarg = f"--user={user}"
locationarg = f"--output={location}"
try:
# run lvechart --period=5d --user=$user --output=$location
subprocess.call(["lvechart", "--period=5d", userarg, locationarg])
os.chown(location, getpwnam(user).pw_uid, getgrnam(user).gr_gid)
print(green(f"Chart successfully generated for {user} at {location}"))
except subprocess.CalledProcessError as error:
print(red(error.output))
def lveinfo(sortmethod, time, columns):
"""Run lve info with the provided arguments
lveinfo -d --period=5m -o $sort --show-columns $columns"""
try:
# fmt: off
data = subprocess.check_output([
"lveinfo", "-d", f"--period={time}", "--json", "-o", sortmethod,
"--show-columns", columns,
])
# fmt: on
return data
except subprocess.CalledProcessError as error:
print(red(error.output))
sys.exit(1)
def tableify(table, data):
"""Format given data into a pretty table"""
for row in data:
table.add_row([key[1] for key in row.items()])
print(table)
def sizeof_fmt(num, suffix='B'):
for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
if abs(num) < 1024.0:
return f"{num:3.1f}{unit}{suffix}"
num /= 1024.0
return "{:.1f}{}{}".format(num, 'Yi', suffix)
def main():
"""Main program logic. Build the arguments for lveinfo, collect the json
output, rearrange and reformat as needed, and send to prettytable for
actual output."""
table = PrettyTable()
args = parse_args()
data = None
if args.Chart:
# skip everything else, we're making a chart for the user
generate_chart(args.User, args.Location)
# collect the json of the desired resource check
elif args.CPU:
print(f"Printing {args.Time} CPU usage statistics...")
resources = ["ID", "uCPU", "lCPU", "CPUf"]
sortmethod = "aCPU"
table.field_names = [
"User",
"AVG used",
"Core Limit",
"Limit Reached #",
]
elif args.Mem:
print(f"Printing {args.Time} Memory usage statistics...")
resources = ["ID", "aPMem", "lPMem", "aVMem", "lVMem", "PMemF", "VMemF"]
sortmethod = "aPMem"
table.field_names = [
"User",
"AVG PMem Used",
"PMem Limit",
"AVG VMem Used",
"VMem Limit",
"Limit Reached #",
]
elif args.Proc:
print(f"Printing {args.Time} process counts...")
resources = ["ID", "aEP", "lEP", "EPf", "aNproc", "lNproc", "NprocF"]
sortmethod = "aNproc"
table.field_names = [
"User",
"Entry Procs",
"EP Limit",
"EP Limit Reached #",
"Total Procs",
"Proc Limit",
"Limit Reached #",
]
elif args.IO:
print(f"Printing {args.Time} IO usage statistics")
resources = [
"ID",
"aIO",
"uIO",
"lIO",
"IOf",
"aIOPS",
"lIOPS",
"IOPSf",
]
sortmethod = "aIO"
table.field_names = [
"User",
"I/O",
"%",
"I/O Limit",
"I/O Limit Reached #",
"I/O Ops.",
"I/O Ops. Limit",
"Ops. Limit Reached #",
]
elif args.Fault:
print(f"Printing usage limits hit in the time period {args.Time}")
resources = [
"ID",
"CPUf",
"EPf",
"NprocF",
"VMemF",
"PMemF",
"IOf",
"IOPSf",
]
sortmethod = "any_faults"
table.field_names = [
"User",
"CPU",
"Entry Procs",
"Total Procs",
"Memory",
"I/O",
"I/O Ops.",
]
if not args.Chart:
# if we're doing anything but generating a chart, run lveinfo
data = lveinfo(sortmethod, args.Time, ",".join(resources))
if data:
# load lveinfo's json output into a dict
data = json.loads(data)
tablelist = []
iteration = 0
for user in data["data"]:
if iteration == USERCOUNT:
break
# the info from lve isn't ordered. Put it in our desired order
# based on the resources list
userinfo = OrderedDict()
mems = {"aPMem", "lPMem", "aVMem", "lVMem"}
for resource in resources:
# some special formatting e.g. sizes and percentages
if resource in ("uCPU", "uIO"):
user[resource] = f"{user[resource]:.0%}"
if resource == "lCPU":
user[resource] = user[resource] / 100.0
if resource in mems and isinstance(user[resource], int):
user[resource] = sizeof_fmt(user[resource])
if resource in ["aIO", "lIO"]:
user[resource] = sizeof_fmt(user[resource] * 1024)
if "MemF" in resource:
# Virtual and Physical Memory faults can be combined
if "Memory" in user:
userinfo["Memory"] += user[resource]
else:
userinfo["Memory"] = user[resource]
else:
userinfo[resource] = user[resource]
tablelist.append(userinfo)
iteration += 1
# send the collected data to pretty table
tableify(table, tablelist)
if __name__ == "__main__":
main()
Zerion Mini Shell 1.0