I wrote a script in python and then cleaned it up with some AI magic, so that it is readable and has comments etc.
The purpose is to generate a report of all sites in /home partition and report totals per domain as well as per user. It then sends an e-mail with the report in both HTML and csv.
Hope others can use it, makes it easy for me to get an overview quickly.
All the configuration items are on the top. Just need to change recipient email at a minimum.
The purpose is to generate a report of all sites in /home partition and report totals per domain as well as per user. It then sends an e-mail with the report in both HTML and csv.
Hope others can use it, makes it easy for me to get an overview quickly.
All the configuration items are on the top. Just need to change recipient email at a minimum.
Python:
#!/usr/bin/python3
import os
import glob
import subprocess
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import socket
from datetime import datetime
import csv
import io
# Configuration items
BASE_DIRECTORY = "/home/" # Base directory to start the search
RECIPIENT_EMAIL = "[email protected]" # Recipient email address
USER_USAGE_THRESHOLD_GB = 2.0 # Usage above this for users is bolded
DOMAIN_TOTAL_THRESHOLD_GB = 10.0 # Total usage above this for domains is bolded
SENDMAIL_PATH = "/usr/sbin/sendmail" # Path to the sendmail executable
EMAIL_SUBJECT = "DA E-mail Usage Report" # Subject of the email
DATE_FORMAT = "%d-%m-%Y" # Format for the date in the email and CSV
HTML_REPORT_TITLE = "DA E-mail Usage Report" # Title of the HTML report
CSV_FILENAME = "disk_usage" # Base filename for the CSV attachment
def get_disk_quota_domain_user_sendmail(
base_directory=BASE_DIRECTORY,
recipient_email=RECIPIENT_EMAIL,
user_threshold=USER_USAGE_THRESHOLD_GB,
domain_threshold=DOMAIN_TOTAL_THRESHOLD_GB,
sendmail_path=SENDMAIL_PATH,
email_subject=EMAIL_SUBJECT,
date_format=DATE_FORMAT,
html_report_title=HTML_REPORT_TITLE,
csv_filename=CSV_FILENAME,
):
"""
Calculates disk usage, formats it as HTML table, and sends it via sendmail with CSV attachment.
Excludes symbolic links from the calculation.
Args:
base_directory (str): The base directory to start the search.
recipient_email (str): The recipient email address.
user_threshold (float): Usage threshold in GB for bolding user usage.
domain_threshold (float): Total usage threshold in GB for bolding domain usage.
sendmail_path (str): Path to the sendmail executable.
email_subject (str): Subject of the email.
date_format (str): Format for the date in the email and CSV.
html_report_title (str): Title of the HTML report.
csv_filename (str): Base filename for the CSV attachment.
Returns:
str: Result message.
"""
html_content = f"<html><body><h1>{html_report_title}</h1>"
csv_output = io.StringIO()
csv_writer = csv.writer(csv_output)
csv_writer.writerow(['Domain', 'User', 'Usage (GB)'])
domain_usage = {} # Dictionary to store total usage per domain
grand_total_usage = 0.0 # Grand total usage
total_users = 0 # Total number of users
if not os.path.exists(base_directory):
return f"Error: Base directory '{base_directory}' does not exist."
first_level_dirs = [
os.path.join(base_directory, item)
for item in os.listdir(base_directory)
if os.path.isdir(os.path.join(base_directory, item)) and not os.path.islink(os.path.join(base_directory, item))
]
for first_level_dir in first_level_dirs:
imap_dirs = [
os.path.join(first_level_dir, item)
for item in os.listdir(first_level_dir)
if os.path.isdir(os.path.join(first_level_dir, item)) and not os.path.islink(os.path.join(first_level_dir, item))
if item == "imap"
]
if not imap_dirs:
continue
second_level_pattern = os.path.join(imap_dirs[0], "*")
second_level_dirs = glob.glob(second_level_pattern)
for second_level_dir in second_level_dirs:
if os.path.isdir(second_level_dir) and not os.path.islink(second_level_dir):
domain = os.path.basename(second_level_dir)
third_level_dirs = [
os.path.join(second_level_dir, item)
for item in os.listdir(second_level_dir)
if os.path.isdir(os.path.join(second_level_dir, item)) and not os.path.islink(os.path.join(second_level_dir, item))
]
domain_total = 0
for third_level in third_level_dirs:
if os.path.exists(third_level) and not os.path.islink(third_level):
user = os.path.basename(third_level)
total_size_bytes_third = 0
for root, dirs, files in os.walk(third_level, followlinks=False): # Important: followlinks=False
for file in files:
total_size_bytes_third += os.path.getsize(os.path.join(root, file))
total_size_gb_third = total_size_bytes_third / (1024 * 1024 * 1024)
csv_writer.writerow([domain, user, f"{total_size_gb_third:.2f}"])
domain_total += total_size_gb_third
grand_total_usage += total_size_gb_third # Accumulate grand total
total_users += 1 # Increment user count
domain_usage[domain] = domain_total
# Sort domains by total usage in descending order
sorted_domains = sorted(domain_usage.items(), key=lambda item: item[1], reverse=True)
# Add grand totals at the top
html_content += f"<p>Total Users: {total_users}, Total Usage: {grand_total_usage:.2f} GB</p>"
html_content += f"<p>User Usage Threshold: {user_threshold:.2f} GB, Domain Total Threshold: {domain_threshold:.2f} GB</p>" # Display thresholds
# Add domain totals table
html_content += "<h2>Domain Totals</h2><table border='1'><tr><th>Domain</th><th>Total Usage (GB)</th></tr>"
for domain, total in sorted_domains:
if total > domain_threshold:
html_content += f"<tr><td>{domain}</td><td><b>{total:.2f} GB</b></td></tr>"
else:
html_content += f"<tr><td>{domain}</td><td>{total:.2f} GB</td></tr>"
html_content += "</table>"
# Add user data table
html_content += "<h2>User Data</h2><table border='1'><tr><th>Domain</th><th>User</th><th>Usage (GB)</th></tr>"
csv_output.seek(0) # Reset CSV reader to the beginning
csv_reader = csv.reader(csv_output)
next(csv_reader) # Skip header row
for row in csv_reader:
domain, user, usage = row
if float(usage) > user_threshold:
html_content += f"<tr><td>{domain}</td><td>{user}</td><td><b>{usage} GB</b></td></tr>"
else:
html_content += f"<tr><td>{domain}</td><td>{user}</td><td>{usage} GB</td></tr>"
html_content += "</table></body></html>"
msg = MIMEMultipart()
hostname = socket.gethostname()
sender = f"root@{hostname}"
msg["From"] = sender
msg["To"] = recipient_email
current_date = datetime.now().strftime(date_format)
msg["Subject"] = f"{email_subject} - {hostname} - {current_date}" # Included hostname in subject
msg.attach(MIMEText(html_content, "html"))
part = MIMEBase("application", "octet-stream")
part.set_payload(csv_output.getvalue().encode())
encoders.encode_base64(part)
part.add_header("Content-Disposition", "attachment", filename=f"disk_usage_{current_date}.csv")
msg.attach(part)
try:
process = subprocess.Popen([sendmail_path, "-t"], stdin=subprocess.PIPE)
process.communicate(msg.as_bytes())
return "Email sent successfully via sendmail!"
except Exception as e:
return f"Error sending email: {e}"
# Example usage:
result = get_disk_quota_domain_user_sendmail()
print(result)