CreateAccount.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. <?php
  2. /**********************************************************************
  3. * ProxmoxVPS developed. (26.03.19)
  4. * *
  5. *
  6. * CREATED BY MODULESGARDEN -> http://modulesgarden.com
  7. * CONTACT -> contact@modulesgarden.com
  8. *
  9. *
  10. * This software is furnished under a license and may be used and copied
  11. * only in accordance with the terms of such license and with the
  12. * inclusion of the above copyright notice. This software or any other
  13. * copies thereof may not be provided or otherwise made available to any
  14. * other person. No title to and ownership of the software is hereby
  15. * transferred.
  16. *
  17. *
  18. **********************************************************************/
  19. namespace ModulesGarden\Servers\ProxmoxVps\App\Http\Actions;
  20. use MGProvision\Proxmox\v2\models\Kvm;
  21. use MGProvision\Proxmox\v2\models\Node;
  22. use MGProvision\Proxmox\v2\repository\ClusterResourcesRepository;
  23. use ModulesGarden\ProxmoxAddon as addon;
  24. use ModulesGarden\ProxmoxAddon\App\Configuration\Addon\Update\AddonUpgradeService;
  25. use ModulesGarden\ProxmoxAddon\App\Models\VmIpAddress;
  26. use ModulesGarden\ProxmoxAddon\App\Services\ApiService;
  27. use ModulesGarden\ProxmoxAddon\App\Services\Utility;
  28. use ModulesGarden\ProxmoxAddon\App\Services\Vps\HostingService;
  29. use ModulesGarden\ProxmoxAddon\App\Services\Vps\NetworkService;
  30. use ModulesGarden\ProxmoxAddon\App\Services\Vps\ProductService;
  31. use ModulesGarden\ProxmoxAddon\App\Services\Vps\UserService;
  32. use ModulesGarden\ProxmoxAddon\Core\Queue\Models\Job;
  33. use ModulesGarden\Servers\ProxmoxVps\App\Enum\CustomField;
  34. use ModulesGarden\Servers\ProxmoxVps\App\Helpers\AppParams;
  35. use ModulesGarden\Servers\ProxmoxVps\App\Helpers\ProxmoxAddonValidator;
  36. use ModulesGarden\Servers\ProxmoxVps\Core\App\Controllers\Instances\AddonController;
  37. use ModulesGarden\Servers\ProxmoxVps\Core\Models\Whmcs\Server;
  38. use ModulesGarden\Servers\ProxmoxVps\Core\UI\Traits\WhmcsParams;
  39. use ModulesGarden\Servers\ProxmoxVps\App\Enum\ConfigurableOption;
  40. class CreateAccount extends AddonController
  41. {
  42. use WhmcsParams;
  43. use ProductService;
  44. use ApiService;
  45. use UserService;
  46. use HostingService;
  47. use addon\App\Traits\Vps\SnippetTrait;
  48. private $errors = [];
  49. /**
  50. * @var AddonUpgradeService
  51. */
  52. private $addonUpgradeService;
  53. /**
  54. * @var Kvm|null
  55. */
  56. private $templateVm;
  57. public function execute($params = null)
  58. {
  59. if(!ProxmoxAddonValidator::isInstalled()){
  60. return ProxmoxAddonValidator::failAsString();
  61. }
  62. (new AppParams())->initFromWhmcsParams();
  63. $this->addonUpgradeService = new AddonUpgradeService();
  64. $this->addonUpgradeService->run();
  65. try
  66. {
  67. //init nameservers
  68. $this->initNameServers();
  69. //Validate
  70. $this->validate();
  71. //Clone validate
  72. if ($this->configuration()->isQemu())
  73. {
  74. $this->cloneValidate();
  75. }
  76. //Check free resources
  77. $this->checkResources();
  78. //update hostname
  79. if($params['customfields']['proxmoxHostname']){
  80. $this->hosting()->domain = $params['customfields']['proxmoxHostname'];
  81. $this->hosting()->save();
  82. }
  83. //throw errors
  84. if ($this->errors)
  85. {
  86. throw new \Exception(implode(", ", $this->errors));
  87. }
  88. //Create User
  89. if (!$this->isUser())
  90. {
  91. $this->userCreate();
  92. }elseif($this->isUser() ){
  93. $this->createUserIfNotExist($this->getUser());
  94. }
  95. //Assing ip
  96. if (!Utility::isIpManagerProxmoxVPSIntegration())
  97. {
  98. if ($this->isCustomIpRequest() && $this->hasCustomIp())
  99. {
  100. $this->addCustomIp();
  101. }
  102. else
  103. {
  104. $networkService = new NetworkService();
  105. list($requestIPv4, $requestIPv6) = $networkService->getIpAddressesRequest();
  106. $node = $this->getNode()->getNode();
  107. $networkService->hasIp($requestIPv4, $requestIPv6, $node)
  108. ->addIp($requestIPv4, $requestIPv6, $node);
  109. $this->customFieldUpdate(CustomField::NODE, $node);
  110. }
  111. }
  112. //automaticaly configure administartor username for window
  113. if($this->templateVm instanceof Kvm &&
  114. $this->configuration()->isAgent() &&
  115. $this->configuration()->isAgentServicePassword() &&
  116. !$this->configuration()->isAgentTemplateUser() &&
  117. preg_match('/w/', $this->templateVm->config()['ostype'])){
  118. $ciuser = $this->templateVm->config()['ciuser'];
  119. $this->hosting()->update(['username' => $ciuser ?: 'Administrator' ]);
  120. }
  121. elseif($this->configuration()->isQemu() && $this->configuration()->getCiuser()){
  122. $this->hosting()->update(['username' =>$this->configuration()->getCiuser() ]);
  123. }
  124. //hosting limit
  125. $this->saveUsageLimit();
  126. //Create task
  127. if ($this->configuration()->isLxc())
  128. {
  129. $taskName = addon\App\Jobs\Vps\CreateLxcJob::class;
  130. }
  131. else
  132. {
  133. if ($this->configuration()->isQemu())
  134. {
  135. $taskName = addon\App\Jobs\Vps\CreateQemuJob::class;
  136. $osTemplate = $this->getWhmcsConfigOption(ConfigurableOption::OS_TEMPLATE, $this->configuration()->getOsTemplate());
  137. if (!empty($osTemplate) && $osTemplate != "0")
  138. {
  139. $taskName = addon\App\Jobs\Vps\CloneQemuJob::class;
  140. }
  141. }
  142. }
  143. $arguments = [
  144. 'hostingId' => $this->getWhmcsParamByKey('serviceid')
  145. ];
  146. if($this->configuration()->isQemu() && $this->hasSnippet()){
  147. $parent = addon\Core\Helper\queue(addon\App\Jobs\Vps\CreateSnippet::class, $arguments, null, "hosting", $this->getWhmcsParamByKey("serviceid"));
  148. }
  149. addon\Core\Helper\queue($taskName, $arguments, $parent->id, "hosting", $this->getWhmcsParamByKey("serviceid"));
  150. return "success";
  151. }
  152. catch (\Exception $ex)
  153. {
  154. if (addon\App\Models\ModuleSettings::isDebug())
  155. {
  156. logModuleCall(
  157. 'ProxmoxVps',
  158. __CLASS__,
  159. [],
  160. null,
  161. $ex->getMessage() . " " . $ex->getTraceAsString()
  162. );
  163. }
  164. return $ex->getMessage();
  165. }
  166. }
  167. private function validate()
  168. {
  169. //vmid
  170. if ($this->getWhmcsCustomField(CustomField::VMID))
  171. {
  172. $this->errors[] = "Custom Field \"VMID\" is not empty";
  173. }
  174. //node
  175. if ($this->getWhmcsCustomField(CustomField::NODE))
  176. {
  177. $this->errors[] = "Custom Field \"Node\" is not empty";
  178. }
  179. if (!in_array($this->configuration()->getVirtualization(), ["qemu", "lxc"]))
  180. {
  181. $this->errors[] = 'Unknown virtualization type: ' . $this->configuration()->getVirtualization();
  182. }
  183. //Network bridge
  184. if (!$this->configuration()->getBridge())
  185. {
  186. $this->errors[] = "''Bridge' - is empty";
  187. }
  188. //lxc
  189. if ($this->configuration()->isLxc())
  190. {
  191. //OS Template
  192. if (!$this->getWhmcsConfigOption(ConfigurableOption::OS_TEMPLATE, $this->configuration()->getOsTemplate()) )
  193. {
  194. $this->errors[] = "'OS Template' - is empty";
  195. }
  196. //Storage
  197. if (!$this->configuration()->getStorage())
  198. {
  199. $this->errors[] = "'Default Storage' - is empty";
  200. }
  201. $taskName = addon\App\Jobs\Vps\CreateLxcJob::class . '@handle';
  202. }
  203. else
  204. {
  205. if ($this->configuration()->isQemu())
  206. {
  207. //searchdomain
  208. if ($this->configuration()->getSearchdomain() && !preg_match('/^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$/', $this->configuration()->getSearchdomain()))
  209. {
  210. $this->errors[] = sprintf('DNS Domain \'%s\' is invalid', $this->configuration()->getSearchdomain());
  211. }
  212. //SSH Key
  213. if ($this->getWhmcsParamByKey('customfields')['sshkeys'] && !preg_match('/ssh\-/', $this->getWhmcsParamByKey('customfields')['sshkeys']))
  214. {
  215. $this->errors[] = ' SSH Public Key is invalid';
  216. }
  217. $taskName = addon\App\Jobs\Vps\CreateQemuJob::class . '@handle';
  218. $osTemplate = $this->getWhmcsConfigOption(ConfigurableOption::OS_TEMPLATE, $this->configuration()->getOsTemplate() );
  219. //clone
  220. if (!empty($osTemplate) && $osTemplate != "0")
  221. {
  222. $taskName = addon\App\Jobs\Vps\CloneQemuJob::class . '@handle';
  223. }
  224. }
  225. }
  226. $query = Job::where("job", $taskName)->whereIn("status", ['waiting', "running", ""])->where("rel_id", $this->getWhmcsParamByKey("serviceid"))->where("rel_type", "hosting")
  227. ->orWhere("job", $taskName)->where("status", 'error')->where("rel_id", $this->getWhmcsParamByKey("serviceid"))->where("rel_type", "hosting")->where("retry_count","<",100);
  228. if ( $query->count() )
  229. {
  230. $this->errors[] = "Job 'create' already exist";
  231. }
  232. //Name servers
  233. if ($this->configuration()->isLxc() || $this->configuration()->isCloudInitServiceNameservers())
  234. {
  235. $ns = [];
  236. for ($i = 1; $i <= 2; $i++)
  237. {
  238. $n = trim($this->hosting()->{"ns{$i}"});
  239. if (!empty($n) && !filter_var($n, FILTER_VALIDATE_IP))
  240. {
  241. $n = gethostbyname($n);
  242. }
  243. if (!empty($n) && !filter_var($n, FILTER_VALIDATE_IP))
  244. {
  245. $ns[] = "'{$n}'";
  246. }
  247. }
  248. if (!empty($ns))
  249. {
  250. $this->errors[] = sprintf(" Name Server value: %s does not look like a valid IP Address", implode(", ", $ns));
  251. }
  252. }
  253. //Disk size
  254. $diskSize = $this->getWhmcsConfigOption(ConfigurableOption::DISK_SIZE);
  255. if ($diskSize && $this->configuration()->getDiskUnit() == "mb" && $diskSize < 1024)
  256. {
  257. $this->errors[] = sprintf("Disk size of %sMB cannot be created", $diskSize);
  258. }
  259. $rate = $this->getWhmcsConfigOption(addon\App\Enum\Vps\ConfigurableOption::NETWORK_RATE) ? $this->getWhmcsConfigOption(addon\App\Enum\Vps\ConfigurableOption::NETWORK_RATE) :$this->configuration()->getRate();
  260. if ($rate && !is_numeric($rate))
  261. {
  262. $this->errors[] = sprintf("Network Rate - invalid format: %s ", $rate) ;
  263. }
  264. }
  265. private function initNameServers()
  266. {
  267. $this->setHostingId($this->getWhmcsParamByKey("serviceid"));
  268. if ($this->configuration()->isServerNameservers())
  269. {
  270. $server = Server::select('id', 'nameserver1ip', 'nameserver2ip')->findOrFail($this->getWhmcsParamByKey('serverid'));
  271. $this->hosting()->ns1 = trim($server->nameserver1ip);
  272. $this->hosting()->ns2 = trim($server->nameserver2ip);
  273. $this->hosting()->save();
  274. }
  275. else
  276. {
  277. if ($this->configuration()->isLxc() || $this->configuration()->isCloudInitServiceNameservers())
  278. {
  279. $domain = $this->getWhmcsCustomField(CustomField::HOSTNAME, $this->getWhmcsParamByKey('domain'));
  280. $update = [];
  281. for ($i = 1; $i <= 2; $i++)
  282. {
  283. $n = trim($this->hosting()->{"ns{$i}"});
  284. if (!empty($n) && !filter_var($n, FILTER_VALIDATE_IP))
  285. {
  286. $host = preg_match('/\./', $n) ? $n : "{$n}.{$domain}";
  287. $n = gethostbyname($host);
  288. if (!$n || !filter_var($n, FILTER_VALIDATE_IP))
  289. {
  290. throw new \Exception(sprintf("Nameserver '%s' cannot be resolved", $host));
  291. }
  292. }
  293. if (!empty($n) && filter_var($n, FILTER_VALIDATE_IP))
  294. {
  295. $update['ns' . $i] = $n;
  296. $this->hosting()->{"ns{$i}"} = $n;
  297. }
  298. }
  299. if ($update)
  300. {
  301. $this->hosting()->update($update);
  302. }
  303. }
  304. }
  305. }
  306. private function isCustomIpRequest()
  307. {
  308. //if there is custom ip on dedicated ip or assinged ips
  309. $model = $this->getWhmcsParamByKey('model');
  310. if (!$model)
  311. {
  312. $model = \WHMCS\Service\Service::where("id", $this->getWhmcsParamByKey('serviceid'))->first();
  313. }
  314. if (!$model->dedicatedip && !$model->assignedips)
  315. {
  316. return false;
  317. }
  318. $ips = $this->getCustomIp();
  319. if (!$ips)
  320. {
  321. return false;
  322. }
  323. foreach ($ips as $k => $ip)
  324. {
  325. if (!filter_var((string)$ip, FILTER_VALIDATE_IP))
  326. {
  327. throw new \Exception(sprintf("IP Address '%s' is not valid", $ip));
  328. }
  329. }
  330. return true;
  331. }
  332. private function getCustomIp()
  333. {
  334. $ips = [];
  335. $model = $this->getWhmcsParamByKey('model');
  336. if (!$model)
  337. {
  338. $model = \WHMCS\Service\Service::where("id", $this->getWhmcsParamByKey('serviceid'))->first();
  339. }
  340. if ($model->dedicatedip)
  341. {
  342. $ips[] = $model->dedicatedip;
  343. }
  344. if ($model->assignedips)
  345. {
  346. $ex = explode(PHP_EOL, $model->assignedips);
  347. foreach ($ex as $ip)
  348. {
  349. if (!$ip)
  350. {
  351. continue;
  352. }
  353. $ips[] = str_replace(["\r", "\n"], ["", ""], $ip);
  354. }
  355. }
  356. return array_diff($ips, [""]);
  357. }
  358. private function hasCustomIp()
  359. {
  360. $nodeName = $this->getNode()->getNode();
  361. $ipAddresses = $this->getCustomIp();
  362. $virtualization = $this->configuration()->isQemu() ? "KVM" : "LXC";
  363. $ipCollections = addon\App\Models\IpAddress::where('hosting_id', '0')
  364. ->where('private', '0')
  365. ->whereIn('ip', $ipAddresses)
  366. ->whereIn('sid', [$this->getWhmcsParamByKey('serverid'), '0'])
  367. ->whereIn('visualization', [$virtualization, 'Auto'])
  368. ->whereIn('node', [$nodeName, '0', ""]);
  369. $count = $ipCollections->count();
  370. return $count == count($ipAddresses);
  371. }
  372. private function addCustomIp()
  373. {
  374. $nodeName = $this->getNode()->getNode();
  375. $ipAddresses = $this->getCustomIp();
  376. $virtualization = $this->configuration()->isQemu() ? "KVM" : "LXC";
  377. addon\App\Models\IpAddress::where('hosting_id', '0')
  378. ->where('private', '0')
  379. ->whereIn('ip', $ipAddresses)
  380. ->whereIn('sid', [$this->getWhmcsParamByKey('serverid'), '0'])
  381. ->whereIn('visualization', [$virtualization, 'Auto'])
  382. ->whereIn('node', [$nodeName, '0', ""])
  383. ->update(["hosting_id" => $this->getWhmcsParamByKey('serviceid'), 'last_check' => Utility::timeStamp()]);
  384. //get ips
  385. $collection = addon\App\Models\IpAddress::where('hosting_id', $this->getWhmcsParamByKey('serviceid'))
  386. ->whereNotIn('ip', function ($query)
  387. {
  388. $query->select('ip')->from((new VmIpAddress)->getTable());
  389. });
  390. foreach ($collection->get() as $ip)
  391. {
  392. /*@var $ip VmIpAddress */
  393. $newIp = new VmIpAddress();
  394. $ipData = $ip->toArray();
  395. $ipData['hosting_id'] = $this->getWhmcsParamByKey('serviceid');
  396. $ipData['server_id'] = $this->getWhmcsParamByKey('serverid');
  397. $newIp->fill($ipData)->save();
  398. }
  399. return $this;
  400. }
  401. private function checkResources()
  402. {
  403. if (!$this->configuration()->isCheckResources())
  404. {
  405. return;
  406. }
  407. //get free space
  408. $storage = $this->configuration()->isQemu() ? $this->configuration()->getDiskStorage() : $this->configuration()->getStorage();
  409. $resurces = $this->getNode()->getFreeSpace($storage);
  410. //Memory
  411. if ($this->getWhmcsConfigOption(ConfigurableOption::MEMORY))
  412. {
  413. $memoryBytes = $this->getWhmcsConfigOption(ConfigurableOption::MEMORY);
  414. Utility::unitFormat($memoryBytes, $this->configuration()->getMemoryUnit(), 'bytes');
  415. }
  416. else
  417. {
  418. $memoryBytes = $this->configuration()->getMemory();
  419. Utility::unitFormat($memoryBytes, "mb", 'bytes');
  420. }
  421. //Validation
  422. if ($memoryBytes && $resurces['memory'] && $memoryBytes > $resurces['memory'])
  423. {
  424. $this->errors[] = sprintf("Unable to allocate %s of Memory. Memory available: %s",
  425. addon\App\Libs\Format::convertBytes($memoryBytes), addon\App\Libs\Format::convertBytes($resurces['memory']));
  426. }
  427. //SWAP
  428. if ($this->configuration()->isLxc())
  429. {
  430. if ($this->getWhmcsConfigOption(ConfigurableOption::SWAP))
  431. {
  432. $swapBytes = $this->getWhmcsConfigOption(ConfigurableOption::SWAP);
  433. Utility::unitFormat($swapBytes, strtolower($this->configuration()->getSwapUnit()), 'bytes');
  434. }
  435. else
  436. {
  437. $swapBytes = $this->configuration()->getSwap();
  438. Utility::unitFormat($swapBytes, "mb", 'bytes');
  439. }
  440. //Validation
  441. if ($swapBytes && $resurces['swap'] && $swapBytes > $resurces['swap'])
  442. {
  443. $this->errors[] = sprintf("Unable to allocate %s of SWAP. SWAP available: %s",
  444. addon\App\Libs\Format::convertBytes($swapBytes), addon\App\Libs\Format::convertBytes($resurces['swap']));
  445. }
  446. }
  447. //Disk
  448. if ($this->getWhmcsConfigOption(ConfigurableOption::DISK_SIZE))
  449. {
  450. $diskBytes = $this->getWhmcsConfigOption(ConfigurableOption::DISK_SIZE);
  451. Utility::unitFormat($diskBytes, $this->configuration()->getDiskUnit(), 'bytes');
  452. }
  453. else
  454. {
  455. $diskBytes = $this->configuration()->getDiskSize();
  456. Utility::unitFormat($diskBytes, "gb", 'bytes');
  457. }
  458. //Validation
  459. if ($diskBytes && $resurces['storage'] && $diskBytes > $resurces['storage'])
  460. {
  461. $this->errors[] = sprintf("Unable to allocate %s of Disk Space. Disk Space available: %s",
  462. addon\App\Libs\Format::convertBytes($diskBytes), addon\App\Libs\Format::convertBytes($resurces['storage']));
  463. }
  464. }
  465. private function cloneValidate()
  466. {
  467. $osTemplate = $this->getWhmcsConfigOption(ConfigurableOption::OS_TEMPLATE, $this->configuration()->getOsTemplate() );
  468. if (empty($osTemplate) && $osTemplate == "0")
  469. {
  470. return;
  471. }
  472. //Support for configurable options i.e vmname|OS Name
  473. if (is_string($osTemplate) && !preg_match('/\//', $osTemplate))
  474. {
  475. $templateNode = $this->getNode()->getNode();
  476. $clusterRepository = new ClusterResourcesRepository();
  477. $clusterRepository->setApi($this->api());
  478. if(!$this->configuration()->isOsTemplatesInAllNodes()){
  479. $clusterRepository->findByNodes([$templateNode]);
  480. }
  481. $clusterRepository->findKvmTemplate();
  482. foreach ($clusterRepository->fetch() as $resurce)
  483. {
  484. if ($resurce->getName() == $osTemplate )
  485. {
  486. $templateVmid = $resurce->getVmid();
  487. $templateNode = $resurce->getNode();
  488. break;
  489. }
  490. }
  491. if (!$templateVmid)
  492. {
  493. $this->errors[] = sprintf("Unable to find KVM template: %s on node: %s", $osTemplate, $this->getNode()->getNode());
  494. return;
  495. }
  496. //Support for configurable options like nodename/vmid|OS Name
  497. }
  498. else
  499. {
  500. if (preg_match('/\//', $osTemplate))
  501. {
  502. list($templateNode, $templateVmid) = explode("/", $osTemplate);
  503. }
  504. }
  505. if (!in_array($templateNode, (array)$this->api()->get_node_list()))
  506. {
  507. $this->errors[] = sprintf("Unable to find node %s on cluster %s", $templateNode, $this->api()->getPveHostname());
  508. return;
  509. }
  510. //template exist on node?
  511. $node = new Node($templateNode);
  512. $node->setApi($this->api());
  513. if (!$node->hasKvm($templateVmid))
  514. {
  515. $this->errors[] = sprintf("KVM template VMID '%s' does not exist on node '%s'", $templateVmid, $templateNode);
  516. return;
  517. }
  518. //Disk size validation
  519. $this->templateVm = new Kvm($templateNode, $templateVmid);
  520. $this->templateVm->setApi($this->api());
  521. //Disk
  522. $diskSize = $this->configuration()->getDiskSize();
  523. if ($this->getWhmcsConfigOption(ConfigurableOption::DISK_SIZE))
  524. {
  525. $diskSize = $this->getWhmcsConfigOption(ConfigurableOption::DISK_SIZE);
  526. Utility::unitFormat($diskSize, $this->configuration()->getDiskUnit(), 'bytes');
  527. }else{
  528. Utility::unitFormat($diskSize, "gb", 'bytes');
  529. }
  530. if ($this->templateVm->getMasterHardDisk()->getBytes() > $diskSize)
  531. {
  532. $this->errors[] = sprintf('Downgrading disk size is restricted. Request Disk size: %s, VM disk size: %s', addon\App\Libs\Format::convertBytes($diskSize), addon\App\Libs\Format::convertBytes($this->templateVm->getMasterHardDisk()->getBytes()));
  533. }
  534. }
  535. }