http://modulesgarden.com * CONTACT -> contact@modulesgarden.com * * * This software is furnished under a license and may be used and copied * only in accordance with the terms of such license and with the * inclusion of the above copyright notice. This software or any other * copies thereof may not be provided or otherwise made available to any * other person. No title to and ownership of the software is hereby * transferred. * * * ******************************************************************** */ namespace ModulesGarden\ProxmoxAddon\App\Cron; use MGProvision\Proxmox\v2\VmFactory; use ModulesGarden\ProxmoxAddon\App\Jobs\Vps\NetworkRate\MaximumVmRateJob; use ModulesGarden\ProxmoxAddon\App\Jobs\Vps\NetworkRate\MinimumVmRateJob; use ModulesGarden\ProxmoxAddon\App\Models\ModuleSetting; use ModulesGarden\ProxmoxAddon\App\Models\ProductConfiguration; use ModulesGarden\ProxmoxAddon\App\Models\VmModel; use ModulesGarden\ProxmoxAddon\App\Models\Whmcs\Hosting; use ModulesGarden\ProxmoxAddon\App\Services\ApiService; use ModulesGarden\ProxmoxAddon\App\Services\Utility; use ModulesGarden\ProxmoxAddon\App\Services\Vps\ProductService; use ModulesGarden\ProxmoxAddon\Core\Api\Whmcs; use ModulesGarden\ProxmoxAddon\Core\CommandLine\Command; use ModulesGarden\ProxmoxAddon\Core\CommandLine\Hypervisor; use ModulesGarden\ProxmoxAddon\Core\Models\Whmcs\Admins; use ModulesGarden\ProxmoxAddon\Core\Models\Whmcs\Server; use ModulesGarden\ProxmoxAddon\Core\UI\Traits\WhmcsParams; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use function ModulesGarden\ProxmoxAddon\Core\Helper\queue; use function ModulesGarden\ProxmoxAddon\Core\Helper\sl; use ModulesGarden\ProxmoxAddon\App\Enum\Vps; use ModulesGarden\ProxmoxAddon\App\Enum\Cloud; /** * Description of Users * * @author Pawel Kopec */ class UsageUpdate extends Command { use WhmcsParams; use ProductService; use ApiService; /** * Command name * @var string */ protected $name = 'update-server-usage'; /** * Command description * @var string */ protected $description = 'Updating Disk & Bandwidth Usage Stat'; /** * Command help text * @var string */ protected $help = ''; protected function process(InputInterface $input, OutputInterface $output, SymfonyStyle $io) { $io->title('Update Server Usage: Starting'); if (!function_exists('ModuleBuildParams')) { require_once ROOTDIR . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . "modulefunctions.php"; } //Update hosting date 0000-00-00 00:00:00 to now $this->updateEmptyLastUpdate(); //init bandwidth reset date to run daily cron job $this->updateEmptyBandwidthResetDate(); //Run daily if($this->isDailyJob()){ //Reset BW $this->resetBandwidth(); $this->unsuspendOverageUsage(); $this->endDailyJob(); } //Update hosting bandwidth $h = (new Hosting())->getTable(); $s = (new Server())->getTable(); $hostings = Hosting::select("{$h}.id", "{$h}.packageid", "{$h}.lastupdate", "{$h}.bwusage", "{$h}.bwlimit", "{$h}.regdate") ->rightJoin($s, "{$h}.server", '=', "{$s}.id") ->where("{$h}.domainstatus", "Active") ->whereIn("{$s}.type", ["proxmoxVPS", "ProxmoxCloudVps"]) ->orderBy("{$h}.server"); $i = 0; /** * @var Hosting $hosting */ foreach ($hostings->get() as $hosting) { $i++; $output->writeln(sprintf("Synchronize hosting: %s", $hosting->id)); try { $params = \ModuleBuildParams($hosting->id); sl("whmcsParams")->setParams($params); unset($this->vm, $this->api, $this->configuration, $this->productId, $this->hostingId); $this->api(); if ($params['moduletype'] == "proxmoxVPS"){ if (!$params['customfields'][Vps\CustomField::VMID]) { throw new \Exception("Custom Field \"vmid\" is empty"); } if (!$params['customfields'][Vps\CustomField::NODE]) { throw new \Exception("Custom Field \"node\" is empty"); } $status = $this->vm()->status(); $rrData = $this->vm()->rrdData(["timeframe" => "hour", "cf" => "AVERAGE"]); $bandwidth = 0; foreach ($rrData as $k => $rrd) { if ($rrd['time'] < $hosting->getLastUpdate()->getTimestamp()) { continue; } $total = ((float)$rrd['netin'] + (float)$rrd['netout']); if ($total > 1000000000) { $total = 0; } $bandwidth += ($total * 57.3); $bandwithTime = date("Y-m-d H:i:s", $rrd['time']); } if (!$bandwithTime) { continue; } $diskUsage = (int)$status['disk']; Utility::unitFormat($bandwidth, "bytes", "mb"); Utility::unitFormat($diskUsage, "bytes", "mb"); $hosting->bwusage += $bandwidth; $hosting->update( [ "diskusage" => $diskUsage, "bwusage" => $hosting->bwusage, "lastupdate" => $bandwithTime ] ); $rate = null; if ($this->getWhmcsConfigOption(Vps\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) && $this->getWhmcsConfigOption(Vps\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) !="-1") { $rate = $this->getWhmcsConfigOption(Vps\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()); } //Minimum VM Network Rate if ($hosting->isBandwidthOverageUsage() && $this->configuration()->getMinimumRate() && $this->vm()->getNetworkDevices($this->configuration()->getBridge())[0] && $this->vm()->getNetworkDevices($this->configuration()->getBridge())[0]->getRate() != $this->configuration()->getMinimumRate()) { queue(MinimumVmRateJob::class, [], null, "hosting", $hosting->id); //Maximum VM Network Rate } else if (!$hosting->isBandwidthOverageUsage() && $this->configuration()->getMinimumRate() && $this->vm()->getNetworkDevices($this->configuration()->getBridge())[0] && $this->vm()->getNetworkDevices($this->configuration()->getBridge())[0]->getRate() != $rate) { queue(MaximumVmRateJob::class, [], null, "hosting", $hosting->id); } //Suspend On Bandwidth Overage if ($this->configuration()->isSuspendOnBandwidthOverage() && $hosting->domainstatus != "Suspended" && $hosting->isBandwidthOverageUsage()) { $localApi = new Whmcs(new Admins()); $localApi->call("modulesuspend", ["accountid" => $hosting->id, "suspendreason" => "Overage Usage"]); $output->writeln(sprintf("Hosting: %s has been suspended, reanson: Overage Usage", $hosting->id)); } $output->writeln(sprintf("Hosting: %s has been synchronized, bandwith usage: %s MB", $hosting->id, $bandwidth)); } if ($params['moduletype'] == "ProxmoxCloudVps"){ $bandwidth = 0; $diskUsage = 0; $rate = null; if ($this->getWhmcsConfigOption(Cloud\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) && $this->getWhmcsConfigOption(Cloud\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) !="-1") { $rate = $this->getWhmcsConfigOption(Cloud\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()); } $vservers = VmModel::ofHostingId($hosting->id); foreach ($vservers->get() as $vserver) { $vm = (new VmFactory())->fromVmModel($vserver); $status = $vm->status(); $diskUsage += (int)$status['disk']; $rrData = $vm->rrdData(["timeframe" => "hour", "cf" => "AVERAGE"]); foreach ($rrData as $k => $rrd) { if ($rrd['time'] < $hosting->getLastUpdate()->getTimestamp()) { continue; } $total = ((float)$rrd['netin'] + (float)$rrd['netout']); if ($total > 1000000000) { $total = 0; } $bandwidth += ($total * 57.3); $bandwithTime = date("Y-m-d H:i:s", $rrd['time']); } if (!$bandwithTime) { continue; } //Minimum VM Network Rate if ($hosting->isBandwidthOverageUsage() && $this->configuration()->getMinimumRate() && $vm->getNetworkDevices($this->configuration()->getBridge())[0] && $vm->getNetworkDevices($this->configuration()->getBridge())[0]->getRate() != $this->configuration()->getMinimumRate()) { queue(\ModulesGarden\ProxmoxAddon\App\Jobs\Cloud\NetworkRate\MinimumVmRateJob::class, [], null, "hosting", $hosting->id, $vserver->id); //Maximum VM Network Rate } else if (!$hosting->isBandwidthOverageUsage() && $this->configuration()->getMinimumRate() && $vm->getNetworkDevices($this->configuration()->getBridge())[0] && $vm->getNetworkDevices($this->configuration()->getBridge())[0]->getRate() != $rate) { queue(\ModulesGarden\ProxmoxAddon\App\Jobs\Cloud\NetworkRate\MaximumVmRateJob::class, [], null, "hosting", $hosting->id, $vserver->id); } } Utility::unitFormat($bandwidth, "bytes", "mb"); Utility::unitFormat($diskUsage, "bytes", "mb"); $hosting->bwusage += $bandwidth; $hosting->update( [ "diskusage" => $diskUsage, "bwusage" => $hosting->bwusage, "lastupdate" => $bandwithTime ] ); //Suspend On Bandwidth Overage if ($this->configuration()->isSuspendOnBandwidthOverage() && $hosting->domainstatus != "Suspended" && $hosting->isBandwidthOverageUsage()) { $localApi = new Whmcs(new Admins()); $localApi->call("modulesuspend", ["accountid" => $hosting->id, "suspendreason" => "Overage Usage"]); $output->writeln(sprintf("Hosting: %s has been suspended, reanson: Overage Usage", $hosting->id)); } $output->writeln(sprintf("Hosting: %s has been synchronized, bandwith usage: %s MB", $hosting->id, $bandwidth)); } } catch (\Exception $ex) { $msg = $ex->getMessage(); if ($hosting) { $msg = sprintf("Hosting Id #%s, %s", $hosting->id, $ex->getMessage()); } $io->error($msg); } (new Hypervisor($this->getName(), $input->getOptions())) ->ping(); } $output->writeln(""); $io->success([ sprintf("Synchronize hostings: %s Entries Processed.", $i), "Update Server Usage: Done" ]); } private function updateEmptyLastUpdate() { $h = 'tblhosting'; Hosting::ofProxmoxVpsAndProxmoxCloud() ->where("{$h}.domainstatus", 'Active') ->where("{$h}.lastupdate", "0000-00-00 00:00:00") ->update(["{$h}.lastupdate" => Hosting::raw('NOW()')]); return $this; } private function updateEmptyBandwidthResetDate() { if (ModuleSetting::bandwidthResetDate()->count()) { return $this; } $setting = new ModuleSetting(); $setting->setting = "bandwidthResetDate"; $setting->value = date("Y-m-d H:i:s"); $setting->save(); return $this; } private function isDailyJob(){ return ModuleSetting::bandwidthResetDate()->whereRaw("DATE(`value`) <= DATE_SUB(NOW(),INTERVAL 24 HOUR) ")->count() > 0; } private function endDailyJob(){ $setting = ModuleSetting::bandwidthResetDate()->firstOrFail(); // save last rest bandwidth date $setting->value = date("Y-m-d H:i:s"); $setting->save(); } private function resetBandwidth() { $productIds = ProductConfiguration::ofSetting('resetUsageFirstDayOfMonth') ->where("value", "like", '\"on\"') ->pluck("product_id") ->all(); $h = 'tblhosting'; Hosting::ofProxmoxVpsAndProxmoxCloud() ->whereIn("{$h}.domainstatus", ["Active", "Suspended"]) ->whereNotIn("{$h}.packageid", $productIds) ->whereRaw(" DAY({$h}.regdate) = DAY(NOW()) ") ->update(["{$h}.bwusage" => 0]); //Reset Usage First Day Of Month if(date("d")=="01"){ Hosting::ofProxmoxVpsAndProxmoxCloud() ->whereIn("{$h}.domainstatus", ["Active", "Suspended"]) ->whereIn("{$h}.packageid", $productIds) ->update(["{$h}.bwusage" => 0]); } } private function unsuspendOverageUsage(){ //get products where option suspendOnBandwidthOverage is on $productIds = ProductConfiguration::ofSetting('suspendOnBandwidthOverage') ->where("value", "like", '\"on\"') ->pluck("product_id") ->all(); if(empty( $productIds)){ return; } //get hosting where domainstatus is Suspended and registration day is now and suspendreason is Overage Usage $h = (new Hosting())->getTable(); $s = (new Server())->getTable(); $hostings = Hosting::select("{$h}.id", "{$h}.packageid", "{$h}.lastupdate", "{$h}.bwusage", "{$h}.bwlimit", "{$h}.regdate") ->rightJoin($s, "{$h}.server", '=', "{$s}.id") ->where("{$h}.domainstatus", "Suspended") ->where("{$h}.suspendreason", "Overage Usage") ->whereIn("{$h}.packageid", $productIds) ->where("{$s}.type", "proxmoxVPS") ->whereRaw(" DAY({$h}.regdate) = DAY(NOW()) "); $i = 0; /** * @var Hosting $hosting */ foreach ($hostings->get() as $hosting) { try{ $localApi = new Whmcs(new Admins()); $localApi->call("ModuleUnsuspend", ["accountid" => $hosting->id]); } catch (\Exception $ex) { echo sprintf("Error: %s, Hosting ID: #%s",$ex->getMessage(), $hosting->id); } } } }