2025-08-01 14:56:30 +05:00
|
|
|
import sys
|
|
|
|
|
sys.path.append('/usr/local/CyberCP')
|
|
|
|
|
import os
|
2025-09-17 00:06:58 +02:00
|
|
|
import gc
|
|
|
|
|
import time
|
2025-08-01 14:56:30 +05:00
|
|
|
from plogical import CyberCPLogFileWriter as logging
|
|
|
|
|
import shlex
|
|
|
|
|
import subprocess
|
|
|
|
|
import validators
|
2025-09-17 00:06:58 +02:00
|
|
|
import resource
|
2025-08-01 14:56:30 +05:00
|
|
|
|
|
|
|
|
class findBWUsage:
|
2025-09-17 00:06:58 +02:00
|
|
|
# Configuration constants
|
|
|
|
|
MAX_MEMORY_MB = 512 # Maximum memory usage in MB
|
|
|
|
|
MAX_PROCESSING_TIME = 300 # Maximum processing time in seconds (5 minutes)
|
|
|
|
|
MAX_LOG_LINES_PER_BATCH = 10000 # Process logs in batches
|
|
|
|
|
MAX_FILE_SIZE_MB = 100 # Skip files larger than 100MB
|
|
|
|
|
|
2025-08-01 14:56:30 +05:00
|
|
|
@staticmethod
|
|
|
|
|
def parse_last_digits(line):
|
2025-09-17 00:06:58 +02:00
|
|
|
"""Safely parse log line and extract bandwidth data"""
|
|
|
|
|
try:
|
|
|
|
|
parts = line.split(' ')
|
|
|
|
|
if len(parts) < 10:
|
|
|
|
|
return None
|
|
|
|
|
# Extract the size field (index 9) and clean it
|
|
|
|
|
size_str = parts[9].replace('"', '').strip()
|
|
|
|
|
if size_str == '-':
|
|
|
|
|
return 0
|
|
|
|
|
return int(size_str)
|
|
|
|
|
except (ValueError, IndexError, AttributeError):
|
|
|
|
|
return None
|
2025-08-01 14:56:30 +05:00
|
|
|
|
|
|
|
|
@staticmethod
|
2025-09-17 00:06:58 +02:00
|
|
|
def get_file_size_mb(filepath):
|
|
|
|
|
"""Get file size in MB"""
|
|
|
|
|
try:
|
|
|
|
|
return os.path.getsize(filepath) / (1024 * 1024)
|
|
|
|
|
except OSError:
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def set_memory_limit():
|
|
|
|
|
"""Set memory limit to prevent system overload"""
|
2025-08-01 14:56:30 +05:00
|
|
|
try:
|
2025-09-17 00:06:58 +02:00
|
|
|
# Set memory limit to MAX_MEMORY_MB
|
|
|
|
|
memory_limit = findBWUsage.MAX_MEMORY_MB * 1024 * 1024 # Convert to bytes
|
|
|
|
|
resource.setrlimit(resource.RLIMIT_AS, (memory_limit, memory_limit))
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(f"Failed to set memory limit: {str(e)}")
|
2025-08-01 14:56:30 +05:00
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
@staticmethod
|
|
|
|
|
def calculateBandwidth(domainName):
|
|
|
|
|
"""Calculate bandwidth usage for a domain with memory protection"""
|
|
|
|
|
start_time = time.time()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
path = "/home/" + domainName + "/logs/" + domainName + ".access_log"
|
|
|
|
|
|
2025-08-01 14:56:30 +05:00
|
|
|
if not os.path.exists(path):
|
|
|
|
|
return 0
|
2025-09-17 00:06:58 +02:00
|
|
|
|
|
|
|
|
# Check file size before processing
|
|
|
|
|
file_size_mb = findBWUsage.get_file_size_mb(path)
|
|
|
|
|
if file_size_mb > findBWUsage.MAX_FILE_SIZE_MB:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(f"Skipping large file {path} ({file_size_mb:.2f}MB)")
|
|
|
|
|
return 0
|
2025-08-01 14:56:30 +05:00
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
if not os.path.exists("/home/" + domainName + "/logs"):
|
2025-08-01 14:56:30 +05:00
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
bwmeta = "/home/cyberpanel/%s.bwmeta" % (domainName)
|
2025-09-17 00:06:58 +02:00
|
|
|
|
|
|
|
|
# Initialize metadata
|
|
|
|
|
currentUsed = 0
|
|
|
|
|
currentLinesRead = 0
|
|
|
|
|
|
|
|
|
|
# Read existing metadata
|
2025-08-01 14:56:30 +05:00
|
|
|
if os.path.exists(bwmeta):
|
2025-09-17 00:06:58 +02:00
|
|
|
try:
|
|
|
|
|
with open(bwmeta, 'r') as f:
|
|
|
|
|
data = f.readlines()
|
|
|
|
|
if len(data) >= 2:
|
|
|
|
|
currentUsed = int(data[0].strip("\n"))
|
|
|
|
|
currentLinesRead = int(data[1].strip("\n"))
|
|
|
|
|
except (ValueError, IndexError):
|
|
|
|
|
currentUsed = 0
|
2025-08-01 14:56:30 +05:00
|
|
|
currentLinesRead = 0
|
|
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
# Process log file in streaming mode to avoid memory issues
|
|
|
|
|
try:
|
|
|
|
|
with open(path, 'r', encoding='utf-8', errors='ignore') as logfile:
|
|
|
|
|
# Skip to the last processed line
|
|
|
|
|
for _ in range(currentLinesRead):
|
|
|
|
|
try:
|
|
|
|
|
next(logfile)
|
|
|
|
|
except StopIteration:
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
lines_processed = 0
|
|
|
|
|
batch_size = 0
|
|
|
|
|
|
|
|
|
|
for line in logfile:
|
|
|
|
|
# Check processing time limit
|
|
|
|
|
if time.time() - start_time > findBWUsage.MAX_PROCESSING_TIME:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(f"Processing timeout for {domainName}")
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
line = line.strip()
|
|
|
|
|
if len(line) > 10:
|
|
|
|
|
bandwidth = findBWUsage.parse_last_digits(line)
|
|
|
|
|
if bandwidth is not None:
|
|
|
|
|
currentUsed += bandwidth
|
|
|
|
|
|
|
|
|
|
currentLinesRead += 1
|
|
|
|
|
lines_processed += 1
|
|
|
|
|
batch_size += 1
|
|
|
|
|
|
|
|
|
|
# Process in batches to manage memory
|
|
|
|
|
if batch_size >= findBWUsage.MAX_LOG_LINES_PER_BATCH:
|
|
|
|
|
# Force garbage collection
|
|
|
|
|
gc.collect()
|
|
|
|
|
batch_size = 0
|
|
|
|
|
|
|
|
|
|
# Check memory usage
|
|
|
|
|
try:
|
|
|
|
|
import psutil
|
|
|
|
|
process = psutil.Process()
|
|
|
|
|
memory_mb = process.memory_info().rss / (1024 * 1024)
|
|
|
|
|
if memory_mb > findBWUsage.MAX_MEMORY_MB:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(f"Memory limit reached for {domainName}")
|
|
|
|
|
break
|
|
|
|
|
except ImportError:
|
|
|
|
|
pass # psutil not available, continue processing
|
|
|
|
|
|
|
|
|
|
except (IOError, OSError) as e:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(f"Error reading log file {path}: {str(e)}")
|
|
|
|
|
return 0
|
2025-08-01 14:56:30 +05:00
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
# Write updated metadata
|
|
|
|
|
try:
|
|
|
|
|
with open(bwmeta, 'w') as f:
|
|
|
|
|
f.write(f"{currentUsed}\n{currentLinesRead}\n")
|
|
|
|
|
os.chmod(bwmeta, 0o600)
|
|
|
|
|
except (IOError, OSError) as e:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(f"Error writing metadata {bwmeta}: {str(e)}")
|
|
|
|
|
return 0
|
2025-08-01 14:56:30 +05:00
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
# Log processing statistics
|
|
|
|
|
processing_time = time.time() - start_time
|
|
|
|
|
if processing_time > 10: # Log if processing took more than 10 seconds
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(f"Processed {domainName}: {lines_processed} lines in {processing_time:.2f}s")
|
2025-08-01 14:56:30 +05:00
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
except Exception as msg:
|
2025-08-01 14:56:30 +05:00
|
|
|
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [calculateBandwidth]")
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def startCalculations():
|
2025-09-17 00:06:58 +02:00
|
|
|
"""Start bandwidth calculations with resource protection"""
|
2025-08-01 14:56:30 +05:00
|
|
|
try:
|
2025-09-17 00:06:58 +02:00
|
|
|
# Set memory limit
|
|
|
|
|
findBWUsage.set_memory_limit()
|
|
|
|
|
|
|
|
|
|
start_time = time.time()
|
|
|
|
|
domains_processed = 0
|
|
|
|
|
|
2025-08-01 14:56:30 +05:00
|
|
|
for directories in os.listdir("/home"):
|
2025-09-17 00:06:58 +02:00
|
|
|
# Check overall processing time
|
|
|
|
|
if time.time() - start_time > findBWUsage.MAX_PROCESSING_TIME * 2:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile("Overall processing timeout reached")
|
|
|
|
|
break
|
|
|
|
|
|
2025-08-01 14:56:30 +05:00
|
|
|
if validators.domain(directories):
|
2025-09-17 00:06:58 +02:00
|
|
|
try:
|
|
|
|
|
result = findBWUsage.calculateBandwidth(directories)
|
|
|
|
|
domains_processed += 1
|
|
|
|
|
|
|
|
|
|
# Force garbage collection after each domain
|
|
|
|
|
gc.collect()
|
|
|
|
|
|
|
|
|
|
# Small delay to prevent system overload
|
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(f"Error processing domain {directories}: {str(e)}")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
total_time = time.time() - start_time
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(f"Bandwidth calculation completed: {domains_processed} domains in {total_time:.2f}s")
|
|
|
|
|
|
|
|
|
|
except Exception as msg:
|
2025-08-01 14:56:30 +05:00
|
|
|
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startCalculations]")
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2025-09-17 00:06:58 +02:00
|
|
|
def findDomainBW(domainName, totalAllowed):
|
|
|
|
|
"""Find domain bandwidth usage with improved error handling"""
|
2025-08-01 14:56:30 +05:00
|
|
|
try:
|
2025-09-17 00:06:58 +02:00
|
|
|
path = "/home/" + domainName + "/logs/" + domainName + ".access_log"
|
2025-08-01 14:56:30 +05:00
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
if not os.path.exists("/home/" + domainName + "/logs"):
|
|
|
|
|
return [0, 0]
|
2025-08-01 14:56:30 +05:00
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
bwmeta = "/home/cyberpanel/%s.bwmeta" % (domainName)
|
2025-08-01 14:56:30 +05:00
|
|
|
|
|
|
|
|
if not os.path.exists(path):
|
2025-09-17 00:06:58 +02:00
|
|
|
return [0, 0]
|
2025-08-01 14:56:30 +05:00
|
|
|
|
|
|
|
|
if os.path.exists(bwmeta):
|
|
|
|
|
try:
|
2025-09-17 00:06:58 +02:00
|
|
|
with open(bwmeta, 'r') as f:
|
|
|
|
|
data = f.readlines()
|
|
|
|
|
|
|
|
|
|
if len(data) < 1:
|
|
|
|
|
return [0, 0]
|
|
|
|
|
|
2025-08-01 14:56:30 +05:00
|
|
|
currentUsed = int(data[0].strip("\n"))
|
2025-09-17 00:06:58 +02:00
|
|
|
inMB = int(float(currentUsed) / (1024.0 * 1024.0))
|
2025-08-01 14:56:30 +05:00
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
if totalAllowed <= 0:
|
|
|
|
|
totalAllowed = 999999
|
2025-08-01 14:56:30 +05:00
|
|
|
|
|
|
|
|
percentage = float(100) / float(totalAllowed)
|
|
|
|
|
percentage = float(percentage) * float(inMB)
|
|
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
if percentage > 100.0:
|
|
|
|
|
percentage = 100
|
2025-08-01 14:56:30 +05:00
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
return [inMB, percentage]
|
|
|
|
|
except (ValueError, IndexError, IOError):
|
|
|
|
|
return [0, 0]
|
2025-08-01 14:56:30 +05:00
|
|
|
else:
|
|
|
|
|
return [0, 0]
|
|
|
|
|
|
|
|
|
|
except OSError as msg:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [findDomainBW]")
|
2025-09-17 00:06:58 +02:00
|
|
|
return [0, 0]
|
2025-08-01 14:56:30 +05:00
|
|
|
except ValueError as msg:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [findDomainBW]")
|
2025-09-17 00:06:58 +02:00
|
|
|
return [0, 0]
|
2025-08-01 14:56:30 +05:00
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def changeSystemLanguage():
|
2025-09-17 00:06:58 +02:00
|
|
|
"""Change system language with improved error handling"""
|
2025-08-01 14:56:30 +05:00
|
|
|
try:
|
|
|
|
|
command = 'localectl set-locale LANG=en_US.UTF-8'
|
|
|
|
|
cmd = shlex.split(command)
|
|
|
|
|
res = subprocess.call(cmd)
|
|
|
|
|
|
|
|
|
|
if res == 1:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile("1440 [setup_cron]")
|
|
|
|
|
else:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
print("###############################################")
|
|
|
|
|
print(" Language Changed to English ")
|
|
|
|
|
print("###############################################")
|
|
|
|
|
|
|
|
|
|
except OSError as msg:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [changeSystemLanguage]")
|
|
|
|
|
return 0
|
|
|
|
|
except ValueError as msg:
|
|
|
|
|
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [changeSystemLanguage]")
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
2025-09-17 00:06:58 +02:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
findBWUsage.startCalculations()
|