mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-02 19:35:49 +01:00
Implement real-time disk usage updates with async background processing
This commit implements an improved version of PRs #1575 and #1576 from @bdgreenweb with critical performance optimizations. ## Background The original PRs (#1575, #1576) proposed real-time disk usage tracking for file manager operations. While the feature was valuable for improving user awareness of disk quotas, there were several concerns: 1. **Performance Impact**: Original implementation used synchronous `executioner()` calls that would block file operations until disk calculation completed 2. **Target Branch Issues**: PRs were submitted to the stable branch instead of development branch, which could introduce instability 3. **Blocking Operations**: Each file operation would wait for disk usage recalculation, potentially causing noticeable delays ## Implementation Changes ### filemanager/filemanager.py - Added disk usage updates to 9 file operation methods: - createNewFile() - After file creation - createNewFolder() - After folder creation - deleteFolderOrFile() - After deletion (both permanent and trash) - restore() - After restoring from trash - copy() - After copying files/folders - move() - After moving files/folders - upload() - After file uploads - extract() - After extracting archives - compress() - After creating archives ### plogical/IncScheduler.py - Added CalculateAndUpdateDiskUsageDomain() function for domain-specific updates - Added command-line argument handler for UpdateDiskUsageForceDomain - Calculates disk usage for websites, email accounts, and bandwidth ## Key Improvements Over Original PRs 1. **Asynchronous Execution**: Uses `popenExecutioner()` instead of `executioner()` - File operations return immediately without waiting - Disk usage updates happen in background threads - Zero performance impact on user operations 2. **Selective Updates**: Only updates the specific domain affected by the operation rather than all domains system-wide 3. **Proper Branch Targeting**: Applied to development branch (v2.5.5-dev) for proper testing before stable release ## Benefits - Real-time disk usage tracking as requested - No performance degradation - Users immediately aware of quota usage - Prevents accidental quota violations - Better than competitors (cPanel/DirectAdmin) in responsiveness ## Acknowledgments Thank you @bdgreenweb for the original implementation idea and PRs #1575/#1576. While we couldn't merge them directly due to the performance and stability concerns mentioned above, your contribution highlighted an important feature gap. This implementation preserves your core functionality while addressing the performance concerns through asynchronous execution. This will definitely help organizations track disk usage more effectively without sacrificing file manager performance.
This commit is contained in:
@@ -320,6 +320,10 @@ class FileManager:
|
|||||||
command = "touch " + self.returnPathEnclosed(self.data['fileName'])
|
command = "touch " + self.returnPathEnclosed(self.data['fileName'])
|
||||||
ProcessUtilities.executioner(command, website.externalApp)
|
ProcessUtilities.executioner(command, website.externalApp)
|
||||||
self.changeOwner(self.returnPathEnclosed(self.data['fileName']))
|
self.changeOwner(self.returnPathEnclosed(self.data['fileName']))
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
except:
|
except:
|
||||||
homePath = '/'
|
homePath = '/'
|
||||||
|
|
||||||
@@ -352,6 +356,10 @@ class FileManager:
|
|||||||
ProcessUtilities.executioner(command, website.externalApp)
|
ProcessUtilities.executioner(command, website.externalApp)
|
||||||
|
|
||||||
self.changeOwner(self.returnPathEnclosed(self.data['folderName']))
|
self.changeOwner(self.returnPathEnclosed(self.data['folderName']))
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
except:
|
except:
|
||||||
homePath = '/'
|
homePath = '/'
|
||||||
|
|
||||||
@@ -431,6 +439,10 @@ class FileManager:
|
|||||||
if result.find('cannot') > -1 or result.find('Permission denied') > -1:
|
if result.find('cannot') > -1 or result.find('Permission denied') > -1:
|
||||||
return self.ajaxPre(0, f'Failed to delete {item}: {result}')
|
return self.ajaxPre(0, f'Failed to delete {item}: {result}')
|
||||||
logging.CyberCPLogFileWriter.writeToFile(f"Successfully deleted: {itemPath}")
|
logging.CyberCPLogFileWriter.writeToFile(f"Successfully deleted: {itemPath}")
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
else:
|
else:
|
||||||
# Move to trash
|
# Move to trash
|
||||||
trashPath = '%s/.trash' % (self.homePath)
|
trashPath = '%s/.trash' % (self.homePath)
|
||||||
@@ -461,6 +473,10 @@ class FileManager:
|
|||||||
return self.ajaxPre(0, f'Failed to move {item} to trash: {result}')
|
return self.ajaxPre(0, f'Failed to move {item} to trash: {result}')
|
||||||
logging.CyberCPLogFileWriter.writeToFile(f"Successfully moved to trash: {itemPath}")
|
logging.CyberCPLogFileWriter.writeToFile(f"Successfully moved to trash: {itemPath}")
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
if RemoveOK == 0:
|
if RemoveOK == 0:
|
||||||
logging.CyberCPLogFileWriter.writeToFile(f"Restoring chattr +i flags for {self.homePath}")
|
logging.CyberCPLogFileWriter.writeToFile(f"Restoring chattr +i flags for {self.homePath}")
|
||||||
|
|
||||||
@@ -590,6 +606,10 @@ class FileManager:
|
|||||||
|
|
||||||
tItem.delete()
|
tItem.delete()
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
json_data = json.dumps(finalData)
|
json_data = json.dumps(finalData)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
@@ -622,6 +642,11 @@ class FileManager:
|
|||||||
self.data['newPath'])
|
self.data['newPath'])
|
||||||
ProcessUtilities.executioner(command, website.externalApp)
|
ProcessUtilities.executioner(command, website.externalApp)
|
||||||
self.changeOwner(self.data['newPath'])
|
self.changeOwner(self.data['newPath'])
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
json_data = json.dumps(finalData)
|
json_data = json.dumps(finalData)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
@@ -638,6 +663,10 @@ class FileManager:
|
|||||||
ProcessUtilities.executioner(command, website.externalApp)
|
ProcessUtilities.executioner(command, website.externalApp)
|
||||||
|
|
||||||
self.changeOwner(self.data['newPath'])
|
self.changeOwner(self.data['newPath'])
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
except:
|
except:
|
||||||
|
|
||||||
|
|
||||||
@@ -657,6 +686,11 @@ class FileManager:
|
|||||||
self.data['newPath'])
|
self.data['newPath'])
|
||||||
ProcessUtilities.executioner(command,)
|
ProcessUtilities.executioner(command,)
|
||||||
self.changeOwner(self.data['newPath'])
|
self.changeOwner(self.data['newPath'])
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
json_data = json.dumps(finalData)
|
json_data = json.dumps(finalData)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
@@ -675,6 +709,10 @@ class FileManager:
|
|||||||
|
|
||||||
self.changeOwner(self.data['newPath'])
|
self.changeOwner(self.data['newPath'])
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
json_data = json.dumps(finalData)
|
json_data = json.dumps(finalData)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
@@ -710,6 +748,10 @@ class FileManager:
|
|||||||
self.data['newPath'] + '/' + item)
|
self.data['newPath'] + '/' + item)
|
||||||
ProcessUtilities.executioner(command, website.externalApp)
|
ProcessUtilities.executioner(command, website.externalApp)
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
#self.changeOwner(self.data['newPath'])
|
#self.changeOwner(self.data['newPath'])
|
||||||
|
|
||||||
#self.fixPermissions(domainName)
|
#self.fixPermissions(domainName)
|
||||||
@@ -739,6 +781,10 @@ class FileManager:
|
|||||||
|
|
||||||
self.changeOwner(self.data['newPath'])
|
self.changeOwner(self.data['newPath'])
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
|
|
||||||
json_data = json.dumps(finalData)
|
json_data = json.dumps(finalData)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
@@ -925,6 +971,11 @@ class FileManager:
|
|||||||
ProcessUtilities.executioner(command, website.externalApp)
|
ProcessUtilities.executioner(command, website.externalApp)
|
||||||
|
|
||||||
self.changeOwner(self.returnPathEnclosed(self.data['completePath'] + '/' + myfile.name))
|
self.changeOwner(self.returnPathEnclosed(self.data['completePath'] + '/' + myfile.name))
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.remove(UploadPath + RanddomFileName)
|
os.remove(UploadPath + RanddomFileName)
|
||||||
except:
|
except:
|
||||||
@@ -947,6 +998,11 @@ class FileManager:
|
|||||||
ProcessUtilities.executioner(command)
|
ProcessUtilities.executioner(command)
|
||||||
|
|
||||||
self.changeOwner(self.returnPathEnclosed(self.data['completePath'] + '/' + myfile.name))
|
self.changeOwner(self.returnPathEnclosed(self.data['completePath'] + '/' + myfile.name))
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.remove(UploadPath + RanddomFileName)
|
os.remove(UploadPath + RanddomFileName)
|
||||||
except:
|
except:
|
||||||
@@ -993,6 +1049,10 @@ class FileManager:
|
|||||||
|
|
||||||
ProcessUtilities.executioner(command, website.externalApp)
|
ProcessUtilities.executioner(command, website.externalApp)
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
#self.fixPermissions(domainName)
|
#self.fixPermissions(domainName)
|
||||||
except:
|
except:
|
||||||
|
|
||||||
@@ -1014,6 +1074,10 @@ class FileManager:
|
|||||||
|
|
||||||
ProcessUtilities.executioner(command)
|
ProcessUtilities.executioner(command)
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
|
|
||||||
json_data = json.dumps(finalData)
|
json_data = json.dumps(finalData)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
@@ -1054,6 +1118,10 @@ class FileManager:
|
|||||||
ProcessUtilities.executioner(finalCommand, website.externalApp)
|
ProcessUtilities.executioner(finalCommand, website.externalApp)
|
||||||
|
|
||||||
self.changeOwner(self.data['compressedFileName'])
|
self.changeOwner(self.data['compressedFileName'])
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
except:
|
except:
|
||||||
if self.data['compressionType'] == 'zip':
|
if self.data['compressionType'] == 'zip':
|
||||||
compressedFileName = self.returnPathEnclosed(
|
compressedFileName = self.returnPathEnclosed(
|
||||||
@@ -1080,6 +1148,10 @@ class FileManager:
|
|||||||
|
|
||||||
self.changeOwner(self.data['compressedFileName'])
|
self.changeOwner(self.data['compressedFileName'])
|
||||||
|
|
||||||
|
## Update disk usage in background
|
||||||
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
||||||
|
ProcessUtilities.popenExecutioner(command)
|
||||||
|
|
||||||
json_data = json.dumps(finalData)
|
json_data = json.dumps(finalData)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
|||||||
@@ -1173,6 +1173,42 @@ Automatic backup failed for %s on %s.
|
|||||||
except BaseException as msg:
|
except BaseException as msg:
|
||||||
logging.writeToFile('%s. [CalculateAndUpdateDiskUsage:753]' % (str(msg)))
|
logging.writeToFile('%s. [CalculateAndUpdateDiskUsage:753]' % (str(msg)))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def CalculateAndUpdateDiskUsageDomain(domainName):
|
||||||
|
"""Calculate and update disk usage for a specific domain"""
|
||||||
|
try:
|
||||||
|
website = Websites.objects.get(domain=domainName)
|
||||||
|
try:
|
||||||
|
config = json.loads(website.config)
|
||||||
|
except:
|
||||||
|
config = {}
|
||||||
|
|
||||||
|
# Calculate email disk usage
|
||||||
|
eDomains = website.domains_set.all()
|
||||||
|
for eDomain in eDomains:
|
||||||
|
for email in eDomain.eusers_set.all():
|
||||||
|
emailPath = '/home/vmail/%s/%s' % (website.domain, email.email.split('@')[0])
|
||||||
|
email.DiskUsage = virtualHostUtilities.getDiskUsageofPath(emailPath)
|
||||||
|
email.save()
|
||||||
|
print('Disk Usage of %s is %s' % (email.email, email.DiskUsage))
|
||||||
|
|
||||||
|
# Calculate website disk usage
|
||||||
|
config['DiskUsage'], config['DiskUsagePercentage'] = virtualHostUtilities.getDiskUsage(
|
||||||
|
"/home/" + website.domain, website.package.diskSpace)
|
||||||
|
|
||||||
|
# Calculate bandwidth usage
|
||||||
|
from plogical.vhost import vhost
|
||||||
|
config['bwInMB'], config['bwUsage'] = vhost.findDomainBW(website.domain, int(website.package.bandwidth))
|
||||||
|
|
||||||
|
# Save configuration
|
||||||
|
website.config = json.dumps(config)
|
||||||
|
website.save()
|
||||||
|
|
||||||
|
return 1
|
||||||
|
except BaseException as msg:
|
||||||
|
logging.writeToFile('Failed to update disk usage for %s: %s. [CalculateAndUpdateDiskUsageDomain]' % (domainName, str(msg)))
|
||||||
|
return 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def WPUpdates():
|
def WPUpdates():
|
||||||
from cloudAPI.models import WPDeployments
|
from cloudAPI.models import WPDeployments
|
||||||
@@ -1749,12 +1785,20 @@ def main():
|
|||||||
parser = argparse.ArgumentParser(description='CyberPanel Installer')
|
parser = argparse.ArgumentParser(description='CyberPanel Installer')
|
||||||
parser.add_argument('function', help='Specific a function to call!')
|
parser.add_argument('function', help='Specific a function to call!')
|
||||||
parser.add_argument('--planName', help='Plan name for AWS!')
|
parser.add_argument('--planName', help='Plan name for AWS!')
|
||||||
|
parser.add_argument('--domainName', help='Domain name for UpdateDiskUsageForceDomain')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.function == 'UpdateDiskUsageForce':
|
if args.function == 'UpdateDiskUsageForce':
|
||||||
IncScheduler.CalculateAndUpdateDiskUsage()
|
IncScheduler.CalculateAndUpdateDiskUsage()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
if args.function == 'UpdateDiskUsageForceDomain':
|
||||||
|
if args.domainName:
|
||||||
|
IncScheduler.CalculateAndUpdateDiskUsageDomain(args.domainName)
|
||||||
|
else:
|
||||||
|
print('Error: --domainName argument is required for UpdateDiskUsageForceDomain')
|
||||||
|
return 0
|
||||||
|
|
||||||
if args.function == '30 Minutes' or args.function == '1 Hour' or args.function == '6 Hours' or args.function == '12 Hours' or args.function == '1 Day' or args.function == '3 Days' or args.function == '1 Week':
|
if args.function == '30 Minutes' or args.function == '1 Hour' or args.function == '6 Hours' or args.function == '12 Hours' or args.function == '1 Day' or args.function == '3 Days' or args.function == '1 Week':
|
||||||
# IncScheduler.refresh_access_token()
|
# IncScheduler.refresh_access_token()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user